@runfusion/fusion 0.24.0 → 0.25.0
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 +2254 -1349
- package/dist/client/assets/AgentDetailView-ZbHEbYRT.js +18 -0
- package/dist/client/assets/{AgentsView-BkB9FiMT.js → AgentsView-B3jYk8Kt.js} +3 -3
- package/dist/client/assets/ChatView-DhPkiEGs.js +1 -0
- package/dist/client/assets/{DevServerView-BkvtjZBa.js → DevServerView-DyGDEiBP.js} +1 -1
- package/dist/client/assets/{DirectoryPicker-BK-KbnhP.js → DirectoryPicker-D5UIeIl6.js} +1 -1
- package/dist/client/assets/{DocumentsView-BEg1CQAk.js → DocumentsView-DNHu1T8K.js} +1 -1
- package/dist/client/assets/{EvalsView-Berf9bQm.js → EvalsView-CpRobtDi.js} +1 -1
- package/dist/client/assets/{ExperimentalAgentOnboardingModal-jcInE50G.js → ExperimentalAgentOnboardingModal-DOY_oZi7.js} +1 -1
- package/dist/client/assets/{InsightsView-BX5bSF1J.js → InsightsView-vp0RE8Mg.js} +1 -1
- package/dist/client/assets/MemoryView-PSc5lGJt.js +2 -0
- package/dist/client/assets/MemoryView-zaXewZzi.css +1 -0
- package/dist/client/assets/{NodesView-DLUOBLf6.js → NodesView-DMj6HGeC.js} +1 -1
- package/dist/client/assets/{PiExtensionsManager-COlJf0Kx.js → PiExtensionsManager-DL_QcN56.js} +2 -2
- package/dist/client/assets/PluginManager-BtYKm8IT.js +1 -0
- package/dist/client/assets/{ResearchView-B256Lr8I.js → ResearchView-BhWqfdV0.js} +1 -1
- package/dist/client/assets/{SettingsModal-BeA_nQtW.js → SettingsModal-BAgB4_AR.js} +4 -4
- package/dist/client/assets/{SettingsModal-yRqM4DV8.js → SettingsModal-CUCyaAyE.js} +1 -1
- package/dist/client/assets/{SetupWizardModal-uUZk3TKT.js → SetupWizardModal-BKscasuh.js} +1 -1
- package/dist/client/assets/{SkillsView-CP8JX0P_.js → SkillsView-BdELqTy7.js} +1 -1
- package/dist/client/assets/{TodoView-DCRIkDZ-.js → TodoView-DFNGBDNV.js} +1 -1
- package/dist/client/assets/{folder-open-DHjELt8-.js → folder-open-k1xmUMyr.js} +1 -1
- package/dist/client/assets/index-Qq2JOOWx.css +1 -0
- package/dist/client/assets/{index-CQyVRLOb.js → index-TFYXEVpn.js} +160 -160
- package/dist/client/assets/{star-DYesq1AV.js → star-ne32r3Y4.js} +1 -1
- package/dist/client/assets/{upload-DTWF3Db5.js → upload-MS-2Gx53.js} +1 -1
- package/dist/client/assets/{users--syrel4l.js → users-C519GSjH.js} +1 -1
- package/dist/client/index.html +2 -2
- package/dist/client/version.json +1 -1
- package/dist/droid-cli/package.json +1 -1
- package/dist/extension.js +1370 -629
- package/dist/pi-claude-cli/package.json +1 -1
- package/dist/plugins/fusion-plugin-cursor-runtime/bundled.js +9 -11
- package/dist/plugins/fusion-plugin-cursor-runtime/package.json +1 -1
- package/dist/plugins/fusion-plugin-dependency-graph/bundled.js +30 -0
- package/dist/plugins/fusion-plugin-dependency-graph/package.json +3 -28
- package/dist/plugins/fusion-plugin-droid-runtime/bundled.js +899 -895
- package/dist/plugins/fusion-plugin-droid-runtime/package.json +1 -1
- package/dist/plugins/fusion-plugin-hermes-runtime/bundled.js +68 -71
- package/dist/plugins/fusion-plugin-hermes-runtime/package.json +1 -1
- package/dist/plugins/fusion-plugin-openclaw-runtime/bundled.js +47 -50
- package/dist/plugins/fusion-plugin-openclaw-runtime/package.json +1 -1
- package/dist/plugins/fusion-plugin-paperclip-runtime/bundled.js +155 -109
- package/dist/plugins/fusion-plugin-paperclip-runtime/package.json +1 -1
- package/dist/plugins/fusion-plugin-reports/package.json +1 -1
- package/dist/plugins/fusion-plugin-reports/src/index.ts +49 -3
- package/dist/plugins/fusion-plugin-reports/src/report-schema.ts +38 -0
- package/dist/plugins/fusion-plugin-reports/src/store/__tests__/report-schema.test.ts +66 -0
- package/dist/plugins/fusion-plugin-reports/src/store/__tests__/report-store.test.ts +177 -0
- package/dist/plugins/fusion-plugin-reports/src/store/report-store.ts +341 -0
- package/dist/plugins/fusion-plugin-reports/src/store/report-types.ts +77 -0
- package/dist/plugins/fusion-plugin-roadmap/package.json +1 -1
- package/dist/plugins/fusion-plugin-whatsapp-chat/package.json +1 -1
- package/package.json +1 -1
- package/dist/client/assets/AgentDetailView-gy_5SUj2.js +0 -18
- package/dist/client/assets/ChatView-B_-B8fqu.js +0 -1
- package/dist/client/assets/MemoryView-CKElJY_3.js +0 -2
- package/dist/client/assets/MemoryView-DiajLXby.css +0 -1
- package/dist/client/assets/PluginManager-CfW55BF4.js +0 -1
- package/dist/client/assets/createLucideIcon-BazL2hk5.js +0 -21
- package/dist/client/assets/dashboard-view-BkTMSZYn.css +0 -1
- package/dist/client/assets/dashboard-view-CyWN-d02.js +0 -63
- package/dist/client/assets/dashboard-view-DdGlfuu-.css +0 -1
- package/dist/client/assets/dashboard-view-lR7YYmSC.js +0 -21
- package/dist/client/assets/index-CxA2Nn0_.css +0 -1
- package/dist/plugins/fusion-plugin-dependency-graph/src/DependencyGraph.css +0 -58
- package/dist/plugins/fusion-plugin-dependency-graph/src/DependencyGraph.tsx +0 -301
- package/dist/plugins/fusion-plugin-dependency-graph/src/GraphHighlight.css +0 -27
- package/dist/plugins/fusion-plugin-dependency-graph/src/GraphTaskNode.css +0 -157
- package/dist/plugins/fusion-plugin-dependency-graph/src/GraphTaskNode.tsx +0 -126
- package/dist/plugins/fusion-plugin-dependency-graph/src/GraphToolbar.css +0 -35
- package/dist/plugins/fusion-plugin-dependency-graph/src/GraphToolbar.tsx +0 -36
- package/dist/plugins/fusion-plugin-dependency-graph/src/__tests__/DependencyGraph.highlighting.test.tsx +0 -112
- package/dist/plugins/fusion-plugin-dependency-graph/src/__tests__/DependencyGraph.persistence.test.tsx +0 -115
- package/dist/plugins/fusion-plugin-dependency-graph/src/__tests__/DependencyGraph.test.tsx +0 -128
- package/dist/plugins/fusion-plugin-dependency-graph/src/__tests__/GraphTaskNode.drag.test.tsx +0 -82
- package/dist/plugins/fusion-plugin-dependency-graph/src/__tests__/GraphTaskNode.test.tsx +0 -307
- package/dist/plugins/fusion-plugin-dependency-graph/src/__tests__/GraphToolbar.test.tsx +0 -60
- package/dist/plugins/fusion-plugin-dependency-graph/src/__tests__/edges.test.tsx +0 -75
- package/dist/plugins/fusion-plugin-dependency-graph/src/__tests__/filtering.test.tsx +0 -62
- package/dist/plugins/fusion-plugin-dependency-graph/src/__tests__/filters.test.ts +0 -78
- package/dist/plugins/fusion-plugin-dependency-graph/src/__tests__/graphPositionStorage.test.ts +0 -95
- package/dist/plugins/fusion-plugin-dependency-graph/src/__tests__/host-integration.test.ts +0 -74
- package/dist/plugins/fusion-plugin-dependency-graph/src/__tests__/index.test.ts +0 -58
- package/dist/plugins/fusion-plugin-dependency-graph/src/__tests__/interactions.test.tsx +0 -121
- package/dist/plugins/fusion-plugin-dependency-graph/src/__tests__/layout.test.ts +0 -70
- package/dist/plugins/fusion-plugin-dependency-graph/src/__tests__/persistence.test.tsx +0 -89
- package/dist/plugins/fusion-plugin-dependency-graph/src/__tests__/useGraphData.test.ts +0 -86
- package/dist/plugins/fusion-plugin-dependency-graph/src/__tests__/useGraphInteraction.test.ts +0 -167
- package/dist/plugins/fusion-plugin-dependency-graph/src/__tests__/useGraphPositions.test.ts +0 -66
- package/dist/plugins/fusion-plugin-dependency-graph/src/__tests__/useNodeDrag.test.ts +0 -81
- package/dist/plugins/fusion-plugin-dependency-graph/src/dashboard-interop.d.ts +0 -35
- package/dist/plugins/fusion-plugin-dependency-graph/src/dashboard-view.tsx +0 -19
- package/dist/plugins/fusion-plugin-dependency-graph/src/edges.tsx +0 -70
- package/dist/plugins/fusion-plugin-dependency-graph/src/filters.ts +0 -8
- package/dist/plugins/fusion-plugin-dependency-graph/src/hooks/__tests__/useDependencyChain.test.ts +0 -53
- package/dist/plugins/fusion-plugin-dependency-graph/src/hooks/useDependencyChain.ts +0 -60
- package/dist/plugins/fusion-plugin-dependency-graph/src/hooks/useGraphPositions.ts +0 -45
- package/dist/plugins/fusion-plugin-dependency-graph/src/hooks/useNodeDrag.ts +0 -114
- package/dist/plugins/fusion-plugin-dependency-graph/src/index.ts +0 -24
- package/dist/plugins/fusion-plugin-dependency-graph/src/layout.ts +0 -91
- package/dist/plugins/fusion-plugin-dependency-graph/src/styles/drag.css +0 -15
- package/dist/plugins/fusion-plugin-dependency-graph/src/types.ts +0 -21
- package/dist/plugins/fusion-plugin-dependency-graph/src/useGraphData.ts +0 -17
- package/dist/plugins/fusion-plugin-dependency-graph/src/useGraphInteraction.ts +0 -292
- package/dist/plugins/fusion-plugin-dependency-graph/src/utils/graphPositionStorage.ts +0 -65
package/dist/extension.js
CHANGED
|
@@ -68,6 +68,8 @@ var init_settings_schema = __esm({
|
|
|
68
68
|
"awaiting-approval",
|
|
69
69
|
"awaiting-user-review",
|
|
70
70
|
"planning-awaiting-input",
|
|
71
|
+
"message:agent-to-user",
|
|
72
|
+
"message:agent-to-agent",
|
|
71
73
|
"gridlock",
|
|
72
74
|
"fallback-used",
|
|
73
75
|
"memory-dreams-processed"
|
|
@@ -274,6 +276,11 @@ var init_settings_schema = __esm({
|
|
|
274
276
|
autoBackupSchedule: "0 2 * * *",
|
|
275
277
|
autoBackupRetention: 7,
|
|
276
278
|
autoBackupDir: ".fusion/backups",
|
|
279
|
+
memoryBackupEnabled: false,
|
|
280
|
+
memoryBackupSchedule: "0 3 * * *",
|
|
281
|
+
memoryBackupRetention: 14,
|
|
282
|
+
memoryBackupDir: ".fusion/backups/memory",
|
|
283
|
+
memoryBackupScope: "all",
|
|
277
284
|
autoSummarizeTitles: false,
|
|
278
285
|
useAiMergeCommitSummary: true,
|
|
279
286
|
titleSummarizerProvider: void 0,
|
|
@@ -4236,6 +4243,7 @@ This means a caller passed a .fusion directory where a project root was expected
|
|
|
4236
4243
|
`INSERT OR IGNORE INTO __meta (key, value) VALUES ('lastModified', '${Date.now()}')`
|
|
4237
4244
|
);
|
|
4238
4245
|
this.migrate();
|
|
4246
|
+
this.ensureTasksSchemaCompatibility();
|
|
4239
4247
|
this.ensureRoutinesSchemaCompatibility();
|
|
4240
4248
|
this.ensureInsightRunsSchemaCompatibility();
|
|
4241
4249
|
this.ensureEvalTaskResultsSchemaCompatibility();
|
|
@@ -4255,6 +4263,27 @@ This means a caller passed a .fusion directory where a project root was expected
|
|
|
4255
4263
|
* Column additions use `hasColumn()` so they are idempotent — safe to
|
|
4256
4264
|
* re-run even if a previous migration partially applied.
|
|
4257
4265
|
*/
|
|
4266
|
+
/**
|
|
4267
|
+
* Applies idempotent compatibility fixes for legacy tasks checkout lease columns.
|
|
4268
|
+
*
|
|
4269
|
+
* FN-3879 documented a self-heal for missing checkout lease columns, but the
|
|
4270
|
+
* original column adds lived only in the `version < 20` migration block. Some
|
|
4271
|
+
* legacy/mesh-synced databases report `schemaVersion >= 20` despite never
|
|
4272
|
+
* receiving those columns, so task listing queries can fail with `no such
|
|
4273
|
+
* column: checkoutNodeId`. Running this unconditionally on init guarantees the
|
|
4274
|
+
* canonical lease columns exist.
|
|
4275
|
+
*/
|
|
4276
|
+
ensureTasksSchemaCompatibility() {
|
|
4277
|
+
if (!this.hasTable("tasks")) {
|
|
4278
|
+
return;
|
|
4279
|
+
}
|
|
4280
|
+
this.addColumnIfMissing("tasks", "checkedOutBy", "TEXT");
|
|
4281
|
+
this.addColumnIfMissing("tasks", "checkedOutAt", "TEXT");
|
|
4282
|
+
this.addColumnIfMissing("tasks", "checkoutNodeId", "TEXT");
|
|
4283
|
+
this.addColumnIfMissing("tasks", "checkoutRunId", "TEXT");
|
|
4284
|
+
this.addColumnIfMissing("tasks", "checkoutLeaseRenewedAt", "TEXT");
|
|
4285
|
+
this.addColumnIfMissing("tasks", "checkoutLeaseEpoch", "INTEGER DEFAULT 0");
|
|
4286
|
+
}
|
|
4258
4287
|
/**
|
|
4259
4288
|
* Applies idempotent compatibility fixes for legacy routines table shapes.
|
|
4260
4289
|
*
|
|
@@ -7301,17 +7330,17 @@ var init_agent_store = __esm({
|
|
|
7301
7330
|
* @returns Matching agent when unambiguous; otherwise null
|
|
7302
7331
|
*/
|
|
7303
7332
|
async resolveAgent(shortname) {
|
|
7304
|
-
const
|
|
7333
|
+
const normalize5 = (value) => value.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
7305
7334
|
const all = await this.listAgents();
|
|
7306
7335
|
const exact = all.find((agent) => agent.id === shortname);
|
|
7307
7336
|
if (exact) {
|
|
7308
7337
|
return exact;
|
|
7309
7338
|
}
|
|
7310
|
-
const normalizedTarget =
|
|
7339
|
+
const normalizedTarget = normalize5(shortname);
|
|
7311
7340
|
if (!normalizedTarget) {
|
|
7312
7341
|
return null;
|
|
7313
7342
|
}
|
|
7314
|
-
const matches = all.filter((agent) =>
|
|
7343
|
+
const matches = all.filter((agent) => normalize5(agent.name) === normalizedTarget);
|
|
7315
7344
|
return matches.length === 1 ? matches[0] : null;
|
|
7316
7345
|
}
|
|
7317
7346
|
// ─────────────────────────────────────────────────────────────────────────
|
|
@@ -8928,9 +8957,9 @@ var init_global_settings = __esm({
|
|
|
8928
8957
|
* Serialize operations via promise chain to prevent lost-update races.
|
|
8929
8958
|
*/
|
|
8930
8959
|
withLock(fn) {
|
|
8931
|
-
let
|
|
8960
|
+
let resolve24;
|
|
8932
8961
|
const next = new Promise((r) => {
|
|
8933
|
-
|
|
8962
|
+
resolve24 = r;
|
|
8934
8963
|
});
|
|
8935
8964
|
const prev = this.lock;
|
|
8936
8965
|
this.lock = next;
|
|
@@ -8938,7 +8967,7 @@ var init_global_settings = __esm({
|
|
|
8938
8967
|
try {
|
|
8939
8968
|
return await fn();
|
|
8940
8969
|
} finally {
|
|
8941
|
-
|
|
8970
|
+
resolve24();
|
|
8942
8971
|
}
|
|
8943
8972
|
});
|
|
8944
8973
|
}
|
|
@@ -31410,8 +31439,8 @@ var require_CronFileParser = __commonJS({
|
|
|
31410
31439
|
* @throws If file cannot be read
|
|
31411
31440
|
*/
|
|
31412
31441
|
static async parseFile(filePath) {
|
|
31413
|
-
const { readFile:
|
|
31414
|
-
const data = await
|
|
31442
|
+
const { readFile: readFile23 } = await Promise.resolve().then(() => __importStar(__require("fs/promises")));
|
|
31443
|
+
const data = await readFile23(filePath, "utf8");
|
|
31415
31444
|
return _CronFileParser.#parseContent(data);
|
|
31416
31445
|
}
|
|
31417
31446
|
/**
|
|
@@ -31641,9 +31670,9 @@ var init_automation_store = __esm({
|
|
|
31641
31670
|
*/
|
|
31642
31671
|
withScheduleLock(id, fn) {
|
|
31643
31672
|
const prev = this.scheduleLocks.get(id) ?? Promise.resolve();
|
|
31644
|
-
let
|
|
31673
|
+
let resolve24;
|
|
31645
31674
|
const next = new Promise((r) => {
|
|
31646
|
-
|
|
31675
|
+
resolve24 = r;
|
|
31647
31676
|
});
|
|
31648
31677
|
this.scheduleLocks.set(id, next);
|
|
31649
31678
|
return prev.then(async () => {
|
|
@@ -31653,7 +31682,7 @@ var init_automation_store = __esm({
|
|
|
31653
31682
|
if (this.scheduleLocks.get(id) === next) {
|
|
31654
31683
|
this.scheduleLocks.delete(id);
|
|
31655
31684
|
}
|
|
31656
|
-
|
|
31685
|
+
resolve24();
|
|
31657
31686
|
}
|
|
31658
31687
|
});
|
|
31659
31688
|
}
|
|
@@ -32362,8 +32391,8 @@ async function writeProjectMemoryFile(rootDir, path2, content) {
|
|
|
32362
32391
|
await mkdir6(dirname5(absPath), { recursive: true });
|
|
32363
32392
|
const tmpPath = `${absPath}.tmp`;
|
|
32364
32393
|
await writeFile5(tmpPath, content, "utf-8");
|
|
32365
|
-
const { rename:
|
|
32366
|
-
await
|
|
32394
|
+
const { rename: rename7 } = await import("node:fs/promises");
|
|
32395
|
+
await rename7(tmpPath, absPath);
|
|
32367
32396
|
return { success: true, backend: "file" };
|
|
32368
32397
|
}
|
|
32369
32398
|
async function listAgentMemoryFiles(rootDir, agentId, date = /* @__PURE__ */ new Date()) {
|
|
@@ -32481,8 +32510,8 @@ async function writeAgentMemoryFile(rootDir, agentId, path2, content) {
|
|
|
32481
32510
|
await mkdir6(dirname5(absPath), { recursive: true });
|
|
32482
32511
|
const tmpPath = `${absPath}.tmp`;
|
|
32483
32512
|
await writeFile5(tmpPath, content, "utf-8");
|
|
32484
|
-
const { rename:
|
|
32485
|
-
await
|
|
32513
|
+
const { rename: rename7 } = await import("node:fs/promises");
|
|
32514
|
+
await rename7(tmpPath, absPath);
|
|
32486
32515
|
return { success: true };
|
|
32487
32516
|
}
|
|
32488
32517
|
function isPathTraversal(path2) {
|
|
@@ -32984,8 +33013,8 @@ var init_memory_backend = __esm({
|
|
|
32984
33013
|
}
|
|
32985
33014
|
const tmpPath = filePath + ".tmp";
|
|
32986
33015
|
await writeFile5(tmpPath, content, "utf-8");
|
|
32987
|
-
const { rename:
|
|
32988
|
-
await
|
|
33016
|
+
const { rename: rename7 } = await import("node:fs/promises");
|
|
33017
|
+
await rename7(tmpPath, filePath);
|
|
32989
33018
|
return {
|
|
32990
33019
|
success: true,
|
|
32991
33020
|
backend: this.type
|
|
@@ -33432,7 +33461,7 @@ var init_project_memory = __esm({
|
|
|
33432
33461
|
// ../core/src/run-command.ts
|
|
33433
33462
|
import { spawn } from "node:child_process";
|
|
33434
33463
|
function runCommandAsync(command, options = {}) {
|
|
33435
|
-
return new Promise((
|
|
33464
|
+
return new Promise((resolve24) => {
|
|
33436
33465
|
const maxBuffer = options.maxBuffer ?? DEFAULT_MAX_BUFFER;
|
|
33437
33466
|
let stdout = "";
|
|
33438
33467
|
let stderr = "";
|
|
@@ -33491,7 +33520,7 @@ function runCommandAsync(command, options = {}) {
|
|
|
33491
33520
|
clearTimeout(forceKillTimer);
|
|
33492
33521
|
forceKillTimer = null;
|
|
33493
33522
|
}
|
|
33494
|
-
|
|
33523
|
+
resolve24({
|
|
33495
33524
|
stdout,
|
|
33496
33525
|
stderr,
|
|
33497
33526
|
exitCode: null,
|
|
@@ -33509,7 +33538,7 @@ function runCommandAsync(command, options = {}) {
|
|
|
33509
33538
|
}
|
|
33510
33539
|
signalProcessGroup("SIGTERM");
|
|
33511
33540
|
scheduleForceKill(NORMAL_CLEANUP_FORCE_KILL_DELAY_MS);
|
|
33512
|
-
|
|
33541
|
+
resolve24({
|
|
33513
33542
|
stdout,
|
|
33514
33543
|
stderr,
|
|
33515
33544
|
exitCode: code,
|
|
@@ -34078,15 +34107,15 @@ function createDistributedTaskIdAllocator(db) {
|
|
|
34078
34107
|
let opLock = Promise.resolve();
|
|
34079
34108
|
const withLock = async (fn) => {
|
|
34080
34109
|
const prev = opLock;
|
|
34081
|
-
let
|
|
34110
|
+
let resolve24;
|
|
34082
34111
|
opLock = new Promise((r) => {
|
|
34083
|
-
|
|
34112
|
+
resolve24 = r;
|
|
34084
34113
|
});
|
|
34085
34114
|
await prev;
|
|
34086
34115
|
try {
|
|
34087
34116
|
return await fn();
|
|
34088
34117
|
} finally {
|
|
34089
|
-
|
|
34118
|
+
resolve24();
|
|
34090
34119
|
}
|
|
34091
34120
|
};
|
|
34092
34121
|
const expireReservations = (nowIso3) => {
|
|
@@ -34361,8 +34390,8 @@ function assertSafeGitBranchName(name) {
|
|
|
34361
34390
|
}
|
|
34362
34391
|
}
|
|
34363
34392
|
function assertSafeAbsolutePath(path2) {
|
|
34364
|
-
const
|
|
34365
|
-
if (!path2 || path2.length > 4096 || !
|
|
34393
|
+
const isAbsolute15 = path2.startsWith("/") || /^[A-Za-z]:[\\/]/.test(path2);
|
|
34394
|
+
if (!path2 || path2.length > 4096 || !isAbsolute15 || path2.startsWith("-") || // Reject shell metacharacters, quotes, control chars, and NULs.
|
|
34366
34395
|
/["'`$\n\r\t;&|<>()*?[\]{}\\\0]/.test(
|
|
34367
34396
|
path2.replace(/^[A-Za-z]:/, "")
|
|
34368
34397
|
// ignore the drive-letter colon on Windows
|
|
@@ -35623,9 +35652,9 @@ ${outcome}`;
|
|
|
35623
35652
|
* lost-update races on the nextId counter.
|
|
35624
35653
|
*/
|
|
35625
35654
|
withConfigLock(fn) {
|
|
35626
|
-
let
|
|
35655
|
+
let resolve24;
|
|
35627
35656
|
const next = new Promise((r) => {
|
|
35628
|
-
|
|
35657
|
+
resolve24 = r;
|
|
35629
35658
|
});
|
|
35630
35659
|
const prev = this.configLock;
|
|
35631
35660
|
this.configLock = next;
|
|
@@ -35633,7 +35662,7 @@ ${outcome}`;
|
|
|
35633
35662
|
try {
|
|
35634
35663
|
return await fn();
|
|
35635
35664
|
} finally {
|
|
35636
|
-
|
|
35665
|
+
resolve24();
|
|
35637
35666
|
}
|
|
35638
35667
|
});
|
|
35639
35668
|
}
|
|
@@ -35642,9 +35671,9 @@ ${outcome}`;
|
|
|
35642
35671
|
* per task ID. Concurrent callers for the same ID will queue behind each other.
|
|
35643
35672
|
*/
|
|
35644
35673
|
withWorktreeAllocationLock(fn) {
|
|
35645
|
-
let
|
|
35674
|
+
let resolve24;
|
|
35646
35675
|
const next = new Promise((r) => {
|
|
35647
|
-
|
|
35676
|
+
resolve24 = r;
|
|
35648
35677
|
});
|
|
35649
35678
|
const prev = this.worktreeAllocationLock;
|
|
35650
35679
|
this.worktreeAllocationLock = next;
|
|
@@ -35652,15 +35681,15 @@ ${outcome}`;
|
|
|
35652
35681
|
try {
|
|
35653
35682
|
return await fn();
|
|
35654
35683
|
} finally {
|
|
35655
|
-
|
|
35684
|
+
resolve24();
|
|
35656
35685
|
}
|
|
35657
35686
|
});
|
|
35658
35687
|
}
|
|
35659
35688
|
withTaskLock(id, fn) {
|
|
35660
35689
|
const prev = this.taskLocks.get(id) ?? Promise.resolve();
|
|
35661
|
-
let
|
|
35690
|
+
let resolve24;
|
|
35662
35691
|
const next = new Promise((r) => {
|
|
35663
|
-
|
|
35692
|
+
resolve24 = r;
|
|
35664
35693
|
});
|
|
35665
35694
|
this.taskLocks.set(id, next);
|
|
35666
35695
|
return prev.then(async () => {
|
|
@@ -35670,7 +35699,7 @@ ${outcome}`;
|
|
|
35670
35699
|
if (this.taskLocks.get(id) === next) {
|
|
35671
35700
|
this.taskLocks.delete(id);
|
|
35672
35701
|
}
|
|
35673
|
-
|
|
35702
|
+
resolve24();
|
|
35674
35703
|
}
|
|
35675
35704
|
});
|
|
35676
35705
|
}
|
|
@@ -37638,8 +37667,8 @@ ${newTask.description}
|
|
|
37638
37667
|
if (this.isWatching) this.taskCache.delete(id);
|
|
37639
37668
|
const dir = this.taskDir(id);
|
|
37640
37669
|
if (existsSync13(dir)) {
|
|
37641
|
-
const { rm:
|
|
37642
|
-
await
|
|
37670
|
+
const { rm: rm5 } = await import("node:fs/promises");
|
|
37671
|
+
await rm5(dir, { recursive: true });
|
|
37643
37672
|
}
|
|
37644
37673
|
for (const dependentTask of rewrittenDependents) {
|
|
37645
37674
|
this.emit("task:updated", dependentTask);
|
|
@@ -37999,8 +38028,8 @@ ${newTask.description}
|
|
|
37999
38028
|
this.clearLinkedAgentTaskIds(id, task.updatedAt);
|
|
38000
38029
|
this.db.prepare("DELETE FROM tasks WHERE id = ?").run(id);
|
|
38001
38030
|
this.db.bumpLastModified();
|
|
38002
|
-
const { rm:
|
|
38003
|
-
await
|
|
38031
|
+
const { rm: rm5 } = await import("node:fs/promises");
|
|
38032
|
+
await rm5(dir, { recursive: true, force: true });
|
|
38004
38033
|
if (this.isWatching) {
|
|
38005
38034
|
this.taskCache.delete(id);
|
|
38006
38035
|
}
|
|
@@ -38163,7 +38192,7 @@ ${newTask.description}
|
|
|
38163
38192
|
}
|
|
38164
38193
|
}
|
|
38165
38194
|
}
|
|
38166
|
-
await new Promise((
|
|
38195
|
+
await new Promise((resolve24) => setImmediate(resolve24));
|
|
38167
38196
|
const selectClause = this.getTaskSelectClause(true);
|
|
38168
38197
|
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();
|
|
38169
38198
|
this.lastPollTime = (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -38183,7 +38212,7 @@ ${newTask.description}
|
|
|
38183
38212
|
this.emit("task:updated", task);
|
|
38184
38213
|
}
|
|
38185
38214
|
if (i > 0 && i % 50 === 0) {
|
|
38186
|
-
await new Promise((
|
|
38215
|
+
await new Promise((resolve24) => setImmediate(resolve24));
|
|
38187
38216
|
}
|
|
38188
38217
|
}
|
|
38189
38218
|
const elapsed = Date.now() - startTime;
|
|
@@ -39138,14 +39167,14 @@ ${newTask.description}
|
|
|
39138
39167
|
if (rows.length === 0) {
|
|
39139
39168
|
return;
|
|
39140
39169
|
}
|
|
39141
|
-
const { rm:
|
|
39170
|
+
const { rm: rm5 } = await import("node:fs/promises");
|
|
39142
39171
|
for (const row of rows) {
|
|
39143
39172
|
const task = this.rowToTask(row);
|
|
39144
39173
|
const archivedAt = task.columnMovedAt ?? task.updatedAt ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
39145
39174
|
const entry = await this.taskToArchiveEntry(task, archivedAt);
|
|
39146
39175
|
this.archiveDb.upsert(entry);
|
|
39147
39176
|
this.db.prepare("DELETE FROM tasks WHERE id = ?").run(task.id);
|
|
39148
|
-
await
|
|
39177
|
+
await rm5(this.taskDir(task.id), { recursive: true, force: true });
|
|
39149
39178
|
if (this.isWatching) {
|
|
39150
39179
|
this.taskCache.delete(task.id);
|
|
39151
39180
|
}
|
|
@@ -39168,8 +39197,8 @@ ${newTask.description}
|
|
|
39168
39197
|
this.archiveDb.upsert(entry);
|
|
39169
39198
|
this.db.prepare("DELETE FROM tasks WHERE id = ?").run(task.id);
|
|
39170
39199
|
this.db.bumpLastModified();
|
|
39171
|
-
const { rm:
|
|
39172
|
-
await
|
|
39200
|
+
const { rm: rm5 } = await import("node:fs/promises");
|
|
39201
|
+
await rm5(dir, { recursive: true, force: true });
|
|
39173
39202
|
if (this.isWatching) {
|
|
39174
39203
|
this.taskCache.delete(task.id);
|
|
39175
39204
|
}
|
|
@@ -40334,7 +40363,7 @@ function runGh(args, cwd) {
|
|
|
40334
40363
|
}
|
|
40335
40364
|
function runGhAsync(args, cwdOrOptions) {
|
|
40336
40365
|
const { cwd, signal: externalSignal, timeoutMs = DEFAULT_GH_TIMEOUT_MS } = normalizeRunGhOptions(cwdOrOptions);
|
|
40337
|
-
return new Promise((
|
|
40366
|
+
return new Promise((resolve24, reject) => {
|
|
40338
40367
|
if (externalSignal?.aborted) {
|
|
40339
40368
|
reject(makeGhError(`gh command aborted: ${describeAbortReason(externalSignal.reason)}`, "ABORT_ERR"));
|
|
40340
40369
|
return;
|
|
@@ -40385,7 +40414,7 @@ function runGhAsync(args, cwdOrOptions) {
|
|
|
40385
40414
|
ghError.stderr = stderr ?? "";
|
|
40386
40415
|
reject(ghError);
|
|
40387
40416
|
} else {
|
|
40388
|
-
|
|
40417
|
+
resolve24(stdout ?? "");
|
|
40389
40418
|
}
|
|
40390
40419
|
}
|
|
40391
40420
|
);
|
|
@@ -40486,7 +40515,7 @@ import { existsSync as existsSync15, readFileSync as readFileSync4, realpathSync
|
|
|
40486
40515
|
import { platform as platform2, tmpdir as tmpdir2 } from "node:os";
|
|
40487
40516
|
import { posix, win32 as win322 } from "node:path";
|
|
40488
40517
|
function runProbe(command, args, timeoutMs) {
|
|
40489
|
-
return new Promise((
|
|
40518
|
+
return new Promise((resolve24) => {
|
|
40490
40519
|
let stdout = "";
|
|
40491
40520
|
let stderr = "";
|
|
40492
40521
|
const child = spawn2(command, args, {
|
|
@@ -40508,11 +40537,11 @@ function runProbe(command, args, timeoutMs) {
|
|
|
40508
40537
|
});
|
|
40509
40538
|
child.on("error", (err) => {
|
|
40510
40539
|
clearTimeout(timer);
|
|
40511
|
-
|
|
40540
|
+
resolve24({ exitCode: null, stdout, stderr: stderr || err.message });
|
|
40512
40541
|
});
|
|
40513
40542
|
child.on("close", (exitCode) => {
|
|
40514
40543
|
clearTimeout(timer);
|
|
40515
|
-
|
|
40544
|
+
resolve24({ exitCode, stdout, stderr });
|
|
40516
40545
|
});
|
|
40517
40546
|
});
|
|
40518
40547
|
}
|
|
@@ -40819,9 +40848,9 @@ var init_routine_store = __esm({
|
|
|
40819
40848
|
*/
|
|
40820
40849
|
withRoutineLock(id, fn) {
|
|
40821
40850
|
const prev = this.routineLocks.get(id) ?? Promise.resolve();
|
|
40822
|
-
let
|
|
40851
|
+
let resolve24;
|
|
40823
40852
|
const next = new Promise((r) => {
|
|
40824
|
-
|
|
40853
|
+
resolve24 = r;
|
|
40825
40854
|
});
|
|
40826
40855
|
this.routineLocks.set(id, next);
|
|
40827
40856
|
return prev.then(async () => {
|
|
@@ -40831,7 +40860,7 @@ var init_routine_store = __esm({
|
|
|
40831
40860
|
if (this.routineLocks.get(id) === next) {
|
|
40832
40861
|
this.routineLocks.delete(id);
|
|
40833
40862
|
}
|
|
40834
|
-
|
|
40863
|
+
resolve24();
|
|
40835
40864
|
}
|
|
40836
40865
|
});
|
|
40837
40866
|
}
|
|
@@ -41567,13 +41596,13 @@ var init_plugin_loader = __esm({
|
|
|
41567
41596
|
* Execute a promise with a timeout.
|
|
41568
41597
|
*/
|
|
41569
41598
|
withTimeout(promise, ms, timeoutMessage) {
|
|
41570
|
-
return new Promise((
|
|
41599
|
+
return new Promise((resolve24, reject) => {
|
|
41571
41600
|
const timer = setTimeout(() => {
|
|
41572
41601
|
reject(new Error(timeoutMessage));
|
|
41573
41602
|
}, ms);
|
|
41574
41603
|
promise.then((result) => {
|
|
41575
41604
|
clearTimeout(timer);
|
|
41576
|
-
|
|
41605
|
+
resolve24(result);
|
|
41577
41606
|
}).catch((err) => {
|
|
41578
41607
|
clearTimeout(timer);
|
|
41579
41608
|
reject(err);
|
|
@@ -42363,8 +42392,329 @@ var init_backup = __esm({
|
|
|
42363
42392
|
}
|
|
42364
42393
|
});
|
|
42365
42394
|
|
|
42395
|
+
// ../core/src/memory-backup.ts
|
|
42396
|
+
import { cp as cp2, mkdir as mkdir9, readdir as readdir7, readFile as readFile10, rename as rename5, rm as rm2, stat as stat4, writeFile as writeFile7 } from "node:fs/promises";
|
|
42397
|
+
import { existsSync as existsSync17 } from "node:fs";
|
|
42398
|
+
import { dirname as dirname7, join as join20, relative as relative3, resolve as resolve10, sep as sep5 } from "node:path";
|
|
42399
|
+
function createMemoryBackupManager(fusionDir, settings) {
|
|
42400
|
+
return new MemoryBackupManager(fusionDir, {
|
|
42401
|
+
backupDir: sanitizeMemoryBackupDir(fusionDir, canonicalizeMemoryBackupDir(settings?.memoryBackupDir)),
|
|
42402
|
+
retention: settings?.memoryBackupRetention,
|
|
42403
|
+
scope: settings?.memoryBackupScope
|
|
42404
|
+
});
|
|
42405
|
+
}
|
|
42406
|
+
async function runMemoryBackupCommand(fusionDir, settings) {
|
|
42407
|
+
if (settings.memoryBackupSchedule && !validateMemoryBackupSchedule(settings.memoryBackupSchedule)) {
|
|
42408
|
+
return { success: false, output: `Invalid memory backup schedule: ${settings.memoryBackupSchedule}` };
|
|
42409
|
+
}
|
|
42410
|
+
const manager = createMemoryBackupManager(fusionDir, settings);
|
|
42411
|
+
try {
|
|
42412
|
+
const backup = await manager.createBackup();
|
|
42413
|
+
const deletedCount = await manager.cleanupOldBackups();
|
|
42414
|
+
const output = deletedCount > 0 ? `Memory backup created: ${backup.filename} (${formatBytes2(backup.size)}). Removed ${deletedCount} old backup(s).` : `Memory backup created: ${backup.filename} (${formatBytes2(backup.size)})`;
|
|
42415
|
+
return { success: true, output, backupPath: backup.path, deletedCount };
|
|
42416
|
+
} catch (error) {
|
|
42417
|
+
return { success: false, output: `Memory backup failed: ${error.message}` };
|
|
42418
|
+
}
|
|
42419
|
+
}
|
|
42420
|
+
async function syncMemoryBackupAutomation(automationStore, settings) {
|
|
42421
|
+
const { AutomationStore: AutomationStore2 } = await Promise.resolve().then(() => (init_automation_store(), automation_store_exports));
|
|
42422
|
+
const schedules = await automationStore.listSchedules();
|
|
42423
|
+
const existing = schedules.find((s) => s.name === MEMORY_BACKUP_SCHEDULE_NAME);
|
|
42424
|
+
if (!settings.memoryBackupEnabled) {
|
|
42425
|
+
if (existing) await automationStore.deleteSchedule(existing.id);
|
|
42426
|
+
return void 0;
|
|
42427
|
+
}
|
|
42428
|
+
const schedule = settings.memoryBackupSchedule || "0 3 * * *";
|
|
42429
|
+
if (!AutomationStore2.isValidCron(schedule)) {
|
|
42430
|
+
throw new Error(`Invalid backup schedule: ${schedule}`);
|
|
42431
|
+
}
|
|
42432
|
+
const command = "fn memory-backup --create";
|
|
42433
|
+
if (existing) {
|
|
42434
|
+
return await automationStore.updateSchedule(existing.id, {
|
|
42435
|
+
scheduleType: "custom",
|
|
42436
|
+
cronExpression: schedule,
|
|
42437
|
+
command,
|
|
42438
|
+
enabled: true
|
|
42439
|
+
});
|
|
42440
|
+
}
|
|
42441
|
+
return await automationStore.createSchedule({
|
|
42442
|
+
name: MEMORY_BACKUP_SCHEDULE_NAME,
|
|
42443
|
+
description: "Automatic memory backup based on project settings",
|
|
42444
|
+
scheduleType: "custom",
|
|
42445
|
+
cronExpression: schedule,
|
|
42446
|
+
command,
|
|
42447
|
+
enabled: true
|
|
42448
|
+
});
|
|
42449
|
+
}
|
|
42450
|
+
async function syncMemoryBackupRoutine(routineStore, settings) {
|
|
42451
|
+
const { RoutineStore: RoutineStore2 } = await Promise.resolve().then(() => (init_routine_store(), routine_store_exports));
|
|
42452
|
+
const routines = await routineStore.listRoutines();
|
|
42453
|
+
const existing = routines.find((routine) => routine.name === MEMORY_BACKUP_SCHEDULE_NAME);
|
|
42454
|
+
if (!settings.memoryBackupEnabled) {
|
|
42455
|
+
if (existing) {
|
|
42456
|
+
await routineStore.deleteRoutine(existing.id);
|
|
42457
|
+
}
|
|
42458
|
+
return void 0;
|
|
42459
|
+
}
|
|
42460
|
+
const schedule = settings.memoryBackupSchedule || "0 3 * * *";
|
|
42461
|
+
if (!RoutineStore2.isValidCron(schedule)) {
|
|
42462
|
+
throw new Error(`Invalid backup schedule: ${schedule}`);
|
|
42463
|
+
}
|
|
42464
|
+
const command = "fn memory-backup --create";
|
|
42465
|
+
if (existing) {
|
|
42466
|
+
return await routineStore.updateRoutine(existing.id, {
|
|
42467
|
+
trigger: { type: "cron", cronExpression: schedule },
|
|
42468
|
+
command,
|
|
42469
|
+
enabled: true
|
|
42470
|
+
});
|
|
42471
|
+
}
|
|
42472
|
+
return await routineStore.createRoutine({
|
|
42473
|
+
name: MEMORY_BACKUP_SCHEDULE_NAME,
|
|
42474
|
+
description: "Automatic memory backup based on project settings",
|
|
42475
|
+
agentId: "",
|
|
42476
|
+
trigger: { type: "cron", cronExpression: schedule },
|
|
42477
|
+
command,
|
|
42478
|
+
enabled: true,
|
|
42479
|
+
scope: "project"
|
|
42480
|
+
});
|
|
42481
|
+
}
|
|
42482
|
+
function canonicalizeMemoryBackupDir(dir) {
|
|
42483
|
+
if (dir === ".kb/backups/memory") return ".fusion/backups/memory";
|
|
42484
|
+
return dir;
|
|
42485
|
+
}
|
|
42486
|
+
function sanitizeMemoryBackupDir(fusionDir, dir) {
|
|
42487
|
+
if (!dir) return void 0;
|
|
42488
|
+
if (dir.startsWith("/") || dir.startsWith("\\") || /^[a-zA-Z]:/.test(dir) || dir.includes("..")) {
|
|
42489
|
+
throw new Error(`Invalid memory backup directory: ${dir}`);
|
|
42490
|
+
}
|
|
42491
|
+
const projectRoot = resolve10(fusionDir, "..");
|
|
42492
|
+
const resolved = resolve10(projectRoot, dir);
|
|
42493
|
+
const rel = relative3(projectRoot, resolved);
|
|
42494
|
+
if (rel.startsWith("..") || rel.includes(`..${sep5}`)) {
|
|
42495
|
+
throw new Error(`Invalid memory backup directory: ${dir}`);
|
|
42496
|
+
}
|
|
42497
|
+
return rel || ".";
|
|
42498
|
+
}
|
|
42499
|
+
function formatTimestamp2(date) {
|
|
42500
|
+
const year = date.getUTCFullYear();
|
|
42501
|
+
const month = String(date.getUTCMonth() + 1).padStart(2, "0");
|
|
42502
|
+
const day = String(date.getUTCDate()).padStart(2, "0");
|
|
42503
|
+
const hours = String(date.getUTCHours()).padStart(2, "0");
|
|
42504
|
+
const minutes = String(date.getUTCMinutes()).padStart(2, "0");
|
|
42505
|
+
const seconds = String(date.getUTCSeconds()).padStart(2, "0");
|
|
42506
|
+
return `${year}-${month}-${day}-${hours}${minutes}${seconds}`;
|
|
42507
|
+
}
|
|
42508
|
+
function formatBytes2(bytes) {
|
|
42509
|
+
if (bytes === 0) return "0 B";
|
|
42510
|
+
const k = 1024;
|
|
42511
|
+
const sizes = ["B", "KB", "MB", "GB"];
|
|
42512
|
+
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
|
42513
|
+
return `${parseFloat((bytes / Math.pow(k, i)).toFixed(2))} ${sizes[i]}`;
|
|
42514
|
+
}
|
|
42515
|
+
async function getDirectoryStats(dir) {
|
|
42516
|
+
const entries = await readdir7(dir, { withFileTypes: true });
|
|
42517
|
+
let size = 0;
|
|
42518
|
+
let entryCount = 0;
|
|
42519
|
+
for (const entry of entries) {
|
|
42520
|
+
const fullPath = join20(dir, entry.name);
|
|
42521
|
+
if (entry.isDirectory()) {
|
|
42522
|
+
const nested = await getDirectoryStats(fullPath);
|
|
42523
|
+
size += nested.size;
|
|
42524
|
+
entryCount += nested.entryCount;
|
|
42525
|
+
continue;
|
|
42526
|
+
}
|
|
42527
|
+
if (entry.isFile()) {
|
|
42528
|
+
const st = await stat4(fullPath);
|
|
42529
|
+
size += st.size;
|
|
42530
|
+
entryCount++;
|
|
42531
|
+
}
|
|
42532
|
+
}
|
|
42533
|
+
return { size, entryCount };
|
|
42534
|
+
}
|
|
42535
|
+
async function listFilesRecursive(root) {
|
|
42536
|
+
const entries = await readdir7(root, { withFileTypes: true });
|
|
42537
|
+
const files = [];
|
|
42538
|
+
for (const entry of entries) {
|
|
42539
|
+
const fullPath = join20(root, entry.name);
|
|
42540
|
+
if (entry.isDirectory()) {
|
|
42541
|
+
files.push(...await listFilesRecursive(fullPath));
|
|
42542
|
+
} else if (entry.isFile()) {
|
|
42543
|
+
files.push(fullPath);
|
|
42544
|
+
}
|
|
42545
|
+
}
|
|
42546
|
+
return files;
|
|
42547
|
+
}
|
|
42548
|
+
var MEMORY_BACKUP_SCHEDULE_NAME, AGENT_MEMORY_ROOT2, MemoryBackupManager, validateMemoryBackupSchedule;
|
|
42549
|
+
var init_memory_backup = __esm({
|
|
42550
|
+
"../core/src/memory-backup.ts"() {
|
|
42551
|
+
"use strict";
|
|
42552
|
+
init_memory_backend();
|
|
42553
|
+
init_backup();
|
|
42554
|
+
MEMORY_BACKUP_SCHEDULE_NAME = "Memory Backup";
|
|
42555
|
+
AGENT_MEMORY_ROOT2 = ".fusion/agent-memory";
|
|
42556
|
+
MemoryBackupManager = class {
|
|
42557
|
+
fusionDir;
|
|
42558
|
+
backupDir;
|
|
42559
|
+
retention;
|
|
42560
|
+
scope;
|
|
42561
|
+
constructor(fusionDir, options) {
|
|
42562
|
+
this.fusionDir = fusionDir;
|
|
42563
|
+
this.backupDir = options?.backupDir ?? ".fusion/backups/memory";
|
|
42564
|
+
this.retention = options?.retention ?? 14;
|
|
42565
|
+
this.scope = options?.scope ?? "all";
|
|
42566
|
+
}
|
|
42567
|
+
getProjectRoot() {
|
|
42568
|
+
return resolve10(this.fusionDir, "..");
|
|
42569
|
+
}
|
|
42570
|
+
getBackupDirPath() {
|
|
42571
|
+
return resolve10(this.getProjectRoot(), this.backupDir);
|
|
42572
|
+
}
|
|
42573
|
+
async collectSources() {
|
|
42574
|
+
const root = this.getProjectRoot();
|
|
42575
|
+
const projectPath = join20(root, MEMORY_WORKSPACE_PATH);
|
|
42576
|
+
const agentsPath = join20(root, AGENT_MEMORY_ROOT2);
|
|
42577
|
+
const includeProject = this.scope !== "agents";
|
|
42578
|
+
const includeAgents = this.scope !== "project";
|
|
42579
|
+
const sources = [];
|
|
42580
|
+
if (includeProject && existsSync17(projectPath)) {
|
|
42581
|
+
sources.push({ source: projectPath, target: "project" });
|
|
42582
|
+
}
|
|
42583
|
+
if (includeAgents && existsSync17(agentsPath)) {
|
|
42584
|
+
sources.push({ source: agentsPath, target: "agents" });
|
|
42585
|
+
}
|
|
42586
|
+
if (sources.length === 0) {
|
|
42587
|
+
throw new Error(`No memory sources found for scope '${this.scope}' (.fusion/memory or .fusion/agent-memory)`);
|
|
42588
|
+
}
|
|
42589
|
+
return sources;
|
|
42590
|
+
}
|
|
42591
|
+
async createBackup() {
|
|
42592
|
+
const backupDirPath = this.getBackupDirPath();
|
|
42593
|
+
await mkdir9(backupDirPath, { recursive: true });
|
|
42594
|
+
const sources = await this.collectSources();
|
|
42595
|
+
const baseName = `memory-${formatTimestamp2(/* @__PURE__ */ new Date())}`;
|
|
42596
|
+
let filename = baseName;
|
|
42597
|
+
let finalPath = join20(backupDirPath, filename);
|
|
42598
|
+
let tmpPath = `${finalPath}.tmp`;
|
|
42599
|
+
let counter = 1;
|
|
42600
|
+
while (existsSync17(finalPath) || existsSync17(tmpPath)) {
|
|
42601
|
+
filename = `${baseName}-${counter}`;
|
|
42602
|
+
finalPath = join20(backupDirPath, filename);
|
|
42603
|
+
tmpPath = `${finalPath}.tmp`;
|
|
42604
|
+
counter++;
|
|
42605
|
+
}
|
|
42606
|
+
try {
|
|
42607
|
+
await mkdir9(tmpPath, { recursive: true });
|
|
42608
|
+
for (const { source, target } of sources) {
|
|
42609
|
+
const targetPath = join20(tmpPath, target);
|
|
42610
|
+
await cp2(source, targetPath, { recursive: true, preserveTimestamps: true });
|
|
42611
|
+
}
|
|
42612
|
+
await rename5(tmpPath, finalPath);
|
|
42613
|
+
} catch (error) {
|
|
42614
|
+
await rm2(tmpPath, { recursive: true, force: true });
|
|
42615
|
+
throw error;
|
|
42616
|
+
}
|
|
42617
|
+
return this.buildInfo(filename, finalPath);
|
|
42618
|
+
}
|
|
42619
|
+
async listBackups() {
|
|
42620
|
+
const backupDirPath = this.getBackupDirPath();
|
|
42621
|
+
try {
|
|
42622
|
+
const entries = await readdir7(backupDirPath);
|
|
42623
|
+
const backups = [];
|
|
42624
|
+
for (const entry of entries) {
|
|
42625
|
+
if (!entry.match(/^memory-\d{4}-\d{2}-\d{2}-\d{6}(?:-\d+)?$/)) continue;
|
|
42626
|
+
const fullPath = join20(backupDirPath, entry);
|
|
42627
|
+
const st = await stat4(fullPath);
|
|
42628
|
+
if (!st.isDirectory()) continue;
|
|
42629
|
+
backups.push(await this.buildInfo(entry, fullPath));
|
|
42630
|
+
}
|
|
42631
|
+
return backups.sort((a, b) => {
|
|
42632
|
+
const timeCompare = b.createdAt.localeCompare(a.createdAt);
|
|
42633
|
+
if (timeCompare !== 0) return timeCompare;
|
|
42634
|
+
return b.filename.localeCompare(a.filename);
|
|
42635
|
+
});
|
|
42636
|
+
} catch {
|
|
42637
|
+
return [];
|
|
42638
|
+
}
|
|
42639
|
+
}
|
|
42640
|
+
async cleanupOldBackups() {
|
|
42641
|
+
const backups = await this.listBackups();
|
|
42642
|
+
if (backups.length <= this.retention) return 0;
|
|
42643
|
+
const sorted = [...backups].sort((a, b) => {
|
|
42644
|
+
const timeCompare = a.createdAt.localeCompare(b.createdAt);
|
|
42645
|
+
if (timeCompare !== 0) return timeCompare;
|
|
42646
|
+
return a.filename.localeCompare(b.filename);
|
|
42647
|
+
});
|
|
42648
|
+
const toDelete = sorted.slice(0, sorted.length - this.retention);
|
|
42649
|
+
let deleted = 0;
|
|
42650
|
+
for (const backup of toDelete) {
|
|
42651
|
+
try {
|
|
42652
|
+
await rm2(backup.path, { recursive: true, force: true });
|
|
42653
|
+
deleted++;
|
|
42654
|
+
} catch {
|
|
42655
|
+
}
|
|
42656
|
+
}
|
|
42657
|
+
return deleted;
|
|
42658
|
+
}
|
|
42659
|
+
async restoreBackup(filename, opts) {
|
|
42660
|
+
const backupRoot = join20(this.getBackupDirPath(), filename);
|
|
42661
|
+
const sourceProject = join20(backupRoot, "project");
|
|
42662
|
+
const sourceAgents = join20(backupRoot, "agents");
|
|
42663
|
+
const hasProject = existsSync17(sourceProject);
|
|
42664
|
+
const hasAgents = existsSync17(sourceAgents);
|
|
42665
|
+
if (!hasProject && !hasAgents) {
|
|
42666
|
+
throw new Error(`Memory backup not found or empty: ${filename}`);
|
|
42667
|
+
}
|
|
42668
|
+
if (hasProject) {
|
|
42669
|
+
await this.restoreTree(sourceProject, join20(this.getProjectRoot(), MEMORY_WORKSPACE_PATH), opts?.overwrite === true);
|
|
42670
|
+
}
|
|
42671
|
+
if (hasAgents) {
|
|
42672
|
+
await this.restoreTree(sourceAgents, join20(this.getProjectRoot(), AGENT_MEMORY_ROOT2), opts?.overwrite === true);
|
|
42673
|
+
}
|
|
42674
|
+
}
|
|
42675
|
+
async restoreTree(sourceDir, destinationDir, overwrite) {
|
|
42676
|
+
const files = await listFilesRecursive(sourceDir);
|
|
42677
|
+
for (const file of files) {
|
|
42678
|
+
const rel = relative3(sourceDir, file);
|
|
42679
|
+
const destFile = join20(destinationDir, rel);
|
|
42680
|
+
await mkdir9(dirname7(destFile), { recursive: true });
|
|
42681
|
+
if (existsSync17(destFile) && !overwrite) {
|
|
42682
|
+
const [srcBuf, dstBuf] = await Promise.all([readFile10(file), readFile10(destFile)]);
|
|
42683
|
+
if (!srcBuf.equals(dstBuf)) {
|
|
42684
|
+
throw new Error(`Restore would overwrite modified memory file: ${destFile}`);
|
|
42685
|
+
}
|
|
42686
|
+
continue;
|
|
42687
|
+
}
|
|
42688
|
+
const tmp = `${destFile}.tmp-${Date.now()}-${Math.random().toString(16).slice(2)}`;
|
|
42689
|
+
const content = await readFile10(file);
|
|
42690
|
+
await writeFile7(tmp, content);
|
|
42691
|
+
await rename5(tmp, destFile);
|
|
42692
|
+
}
|
|
42693
|
+
}
|
|
42694
|
+
async buildInfo(filename, fullPath) {
|
|
42695
|
+
const stats = await stat4(fullPath);
|
|
42696
|
+
const sizeAndCount = await getDirectoryStats(fullPath);
|
|
42697
|
+
const match = filename.match(/^memory-(\d{4})-(\d{2})-(\d{2})-(\d{2})(\d{2})(\d{2})(?:-\d+)?$/);
|
|
42698
|
+
const createdAt = match ? `${match[1]}-${match[2]}-${match[3]}T${match[4]}:${match[5]}:${match[6]}Z` : stats.mtime.toISOString();
|
|
42699
|
+
const hasProject = existsSync17(join20(fullPath, "project"));
|
|
42700
|
+
const hasAgents = existsSync17(join20(fullPath, "agents"));
|
|
42701
|
+
const scope = hasProject && hasAgents ? "all" : hasProject ? "project" : "agents";
|
|
42702
|
+
return {
|
|
42703
|
+
filename,
|
|
42704
|
+
createdAt,
|
|
42705
|
+
size: sizeAndCount.size,
|
|
42706
|
+
path: fullPath,
|
|
42707
|
+
scope,
|
|
42708
|
+
entryCount: sizeAndCount.entryCount
|
|
42709
|
+
};
|
|
42710
|
+
}
|
|
42711
|
+
};
|
|
42712
|
+
validateMemoryBackupSchedule = validateBackupSchedule;
|
|
42713
|
+
}
|
|
42714
|
+
});
|
|
42715
|
+
|
|
42366
42716
|
// ../core/src/settings-export.ts
|
|
42367
|
-
import { writeFile as
|
|
42717
|
+
import { writeFile as writeFile8, readFile as readFile11, rename as rename6 } from "node:fs/promises";
|
|
42368
42718
|
function validateImportData(data) {
|
|
42369
42719
|
const errors = [];
|
|
42370
42720
|
if (data === null || typeof data !== "object") {
|
|
@@ -42486,7 +42836,7 @@ async function importSettings(store, data, options = {}) {
|
|
|
42486
42836
|
}
|
|
42487
42837
|
}
|
|
42488
42838
|
async function readExportFile(filePath) {
|
|
42489
|
-
const content = await
|
|
42839
|
+
const content = await readFile11(filePath, "utf-8");
|
|
42490
42840
|
try {
|
|
42491
42841
|
const parsed = JSON.parse(content);
|
|
42492
42842
|
return parsed;
|
|
@@ -42496,8 +42846,8 @@ async function readExportFile(filePath) {
|
|
|
42496
42846
|
}
|
|
42497
42847
|
async function writeExportFile(filePath, data) {
|
|
42498
42848
|
const tmpPath = filePath + ".tmp";
|
|
42499
|
-
await
|
|
42500
|
-
await
|
|
42849
|
+
await writeFile8(tmpPath, JSON.stringify(data, null, 2));
|
|
42850
|
+
await rename6(tmpPath, filePath);
|
|
42501
42851
|
}
|
|
42502
42852
|
var init_settings_export = __esm({
|
|
42503
42853
|
"../core/src/settings-export.ts"() {
|
|
@@ -42756,7 +43106,7 @@ var init_mission_types = __esm({
|
|
|
42756
43106
|
// ../core/src/docker-client.ts
|
|
42757
43107
|
import Docker from "dockerode";
|
|
42758
43108
|
import { exec } from "node:child_process";
|
|
42759
|
-
import { readFile as
|
|
43109
|
+
import { readFile as readFile12 } from "node:fs/promises";
|
|
42760
43110
|
import { promisify as promisify2 } from "node:util";
|
|
42761
43111
|
function isLocalDaemonHost(host) {
|
|
42762
43112
|
return !host || host.trim() === "" || host === "unix:///var/run/docker.sock";
|
|
@@ -42797,9 +43147,9 @@ var init_docker_client = __esm({
|
|
|
42797
43147
|
const options = {
|
|
42798
43148
|
host: hostConfig.host
|
|
42799
43149
|
};
|
|
42800
|
-
if (hostConfig.tlsCaPath) options.ca = await
|
|
42801
|
-
if (hostConfig.tlsCertPath) options.cert = await
|
|
42802
|
-
if (hostConfig.tlsKeyPath) options.key = await
|
|
43150
|
+
if (hostConfig.tlsCaPath) options.ca = await readFile12(hostConfig.tlsCaPath);
|
|
43151
|
+
if (hostConfig.tlsCertPath) options.cert = await readFile12(hostConfig.tlsCertPath);
|
|
43152
|
+
if (hostConfig.tlsKeyPath) options.key = await readFile12(hostConfig.tlsKeyPath);
|
|
42803
43153
|
if (hostConfig.tlsVerify === false) options.rejectUnauthorized = false;
|
|
42804
43154
|
return new Docker(options);
|
|
42805
43155
|
}
|
|
@@ -43063,7 +43413,7 @@ var init_mesh_config_generator = __esm({
|
|
|
43063
43413
|
hostConfig
|
|
43064
43414
|
}
|
|
43065
43415
|
);
|
|
43066
|
-
await new Promise((
|
|
43416
|
+
await new Promise((resolve24) => setTimeout(resolve24, POST_RECREATE_DELAY_MS));
|
|
43067
43417
|
await this.deps.central.updateManagedDockerNode(managedNodeId, {
|
|
43068
43418
|
apiKey: config.nodeApiKey,
|
|
43069
43419
|
reachableUrl: config.reachableUrl,
|
|
@@ -43171,7 +43521,7 @@ var init_mesh_config_generator = __esm({
|
|
|
43171
43521
|
}
|
|
43172
43522
|
} catch {
|
|
43173
43523
|
}
|
|
43174
|
-
await new Promise((
|
|
43524
|
+
await new Promise((resolve24) => setTimeout(resolve24, intervalMs));
|
|
43175
43525
|
}
|
|
43176
43526
|
return { healthy: false };
|
|
43177
43527
|
}
|
|
@@ -43215,10 +43565,10 @@ var init_docker_provisioning = __esm({
|
|
|
43215
43565
|
}
|
|
43216
43566
|
} : void 0;
|
|
43217
43567
|
const stream = await docker.pull(imageRef, authOptions);
|
|
43218
|
-
await new Promise((
|
|
43568
|
+
await new Promise((resolve24, reject) => {
|
|
43219
43569
|
docker.modem.followProgress(
|
|
43220
43570
|
stream,
|
|
43221
|
-
(err) => err ? reject(err) :
|
|
43571
|
+
(err) => err ? reject(err) : resolve24()
|
|
43222
43572
|
);
|
|
43223
43573
|
});
|
|
43224
43574
|
} catch (error) {
|
|
@@ -43449,36 +43799,36 @@ var init_docker_provisioning = __esm({
|
|
|
43449
43799
|
});
|
|
43450
43800
|
|
|
43451
43801
|
// ../core/src/memory-insights.ts
|
|
43452
|
-
import { readFile as
|
|
43453
|
-
import { existsSync as
|
|
43454
|
-
import { dirname as
|
|
43802
|
+
import { readFile as readFile13, writeFile as writeFile9, mkdir as mkdir10, unlink as unlink5 } from "node:fs/promises";
|
|
43803
|
+
import { existsSync as existsSync18 } from "node:fs";
|
|
43804
|
+
import { dirname as dirname8, join as join21 } from "node:path";
|
|
43455
43805
|
async function readWorkingMemory(rootDir) {
|
|
43456
|
-
const filePath =
|
|
43457
|
-
if (!
|
|
43806
|
+
const filePath = join21(rootDir, MEMORY_WORKING_PATH);
|
|
43807
|
+
if (!existsSync18(filePath)) {
|
|
43458
43808
|
return "";
|
|
43459
43809
|
}
|
|
43460
|
-
return
|
|
43810
|
+
return readFile13(filePath, "utf-8");
|
|
43461
43811
|
}
|
|
43462
43812
|
async function migrateLegacyArtifactIfNeeded(rootDir, canonicalPath, legacyPath) {
|
|
43463
|
-
const canonicalFilePath =
|
|
43464
|
-
const legacyFilePath =
|
|
43465
|
-
if (
|
|
43813
|
+
const canonicalFilePath = join21(rootDir, canonicalPath);
|
|
43814
|
+
const legacyFilePath = join21(rootDir, legacyPath);
|
|
43815
|
+
if (existsSync18(canonicalFilePath) || !existsSync18(legacyFilePath)) {
|
|
43466
43816
|
return;
|
|
43467
43817
|
}
|
|
43468
|
-
const content = await
|
|
43469
|
-
const canonicalDir =
|
|
43470
|
-
if (!
|
|
43471
|
-
await
|
|
43818
|
+
const content = await readFile13(legacyFilePath, "utf-8");
|
|
43819
|
+
const canonicalDir = dirname8(canonicalFilePath);
|
|
43820
|
+
if (!existsSync18(canonicalDir)) {
|
|
43821
|
+
await mkdir10(canonicalDir, { recursive: true });
|
|
43472
43822
|
}
|
|
43473
|
-
await
|
|
43823
|
+
await writeFile9(canonicalFilePath, content, "utf-8");
|
|
43474
43824
|
try {
|
|
43475
43825
|
await unlink5(legacyFilePath);
|
|
43476
43826
|
} catch {
|
|
43477
43827
|
}
|
|
43478
43828
|
}
|
|
43479
43829
|
async function removeLegacyArtifactIfPresent(rootDir, legacyPath) {
|
|
43480
|
-
const legacyFilePath =
|
|
43481
|
-
if (!
|
|
43830
|
+
const legacyFilePath = join21(rootDir, legacyPath);
|
|
43831
|
+
if (!existsSync18(legacyFilePath)) {
|
|
43482
43832
|
return;
|
|
43483
43833
|
}
|
|
43484
43834
|
try {
|
|
@@ -43488,54 +43838,54 @@ async function removeLegacyArtifactIfPresent(rootDir, legacyPath) {
|
|
|
43488
43838
|
}
|
|
43489
43839
|
async function readInsightsMemory(rootDir) {
|
|
43490
43840
|
await migrateLegacyArtifactIfNeeded(rootDir, MEMORY_INSIGHTS_PATH, LEGACY_MEMORY_INSIGHTS_PATH);
|
|
43491
|
-
const filePath =
|
|
43492
|
-
if (!
|
|
43841
|
+
const filePath = join21(rootDir, MEMORY_INSIGHTS_PATH);
|
|
43842
|
+
if (!existsSync18(filePath)) {
|
|
43493
43843
|
return null;
|
|
43494
43844
|
}
|
|
43495
|
-
return
|
|
43845
|
+
return readFile13(filePath, "utf-8");
|
|
43496
43846
|
}
|
|
43497
43847
|
async function writeInsightsMemory(rootDir, content) {
|
|
43498
|
-
const filePath =
|
|
43499
|
-
const dir =
|
|
43500
|
-
if (!
|
|
43501
|
-
await
|
|
43848
|
+
const filePath = join21(rootDir, MEMORY_INSIGHTS_PATH);
|
|
43849
|
+
const dir = dirname8(filePath);
|
|
43850
|
+
if (!existsSync18(dir)) {
|
|
43851
|
+
await mkdir10(dir, { recursive: true });
|
|
43502
43852
|
}
|
|
43503
|
-
await
|
|
43853
|
+
await writeFile9(filePath, content, "utf-8");
|
|
43504
43854
|
await removeLegacyArtifactIfPresent(rootDir, LEGACY_MEMORY_INSIGHTS_PATH);
|
|
43505
43855
|
}
|
|
43506
43856
|
async function writeWorkingMemory(rootDir, content) {
|
|
43507
|
-
const filePath =
|
|
43508
|
-
const dir =
|
|
43509
|
-
if (!
|
|
43510
|
-
await
|
|
43857
|
+
const filePath = join21(rootDir, MEMORY_WORKING_PATH);
|
|
43858
|
+
const dir = dirname8(filePath);
|
|
43859
|
+
if (!existsSync18(dir)) {
|
|
43860
|
+
await mkdir10(dir, { recursive: true });
|
|
43511
43861
|
}
|
|
43512
|
-
await
|
|
43862
|
+
await writeFile9(filePath, content, "utf-8");
|
|
43513
43863
|
}
|
|
43514
43864
|
async function readMemoryAudit(rootDir) {
|
|
43515
43865
|
await migrateLegacyArtifactIfNeeded(rootDir, MEMORY_AUDIT_PATH, LEGACY_MEMORY_AUDIT_PATH);
|
|
43516
|
-
const filePath =
|
|
43517
|
-
if (!
|
|
43866
|
+
const filePath = join21(rootDir, MEMORY_AUDIT_PATH);
|
|
43867
|
+
if (!existsSync18(filePath)) {
|
|
43518
43868
|
return null;
|
|
43519
43869
|
}
|
|
43520
|
-
return
|
|
43870
|
+
return readFile13(filePath, "utf-8");
|
|
43521
43871
|
}
|
|
43522
43872
|
async function writeMemoryAudit(rootDir, content) {
|
|
43523
|
-
const filePath =
|
|
43524
|
-
const dir =
|
|
43525
|
-
if (!
|
|
43526
|
-
await
|
|
43873
|
+
const filePath = join21(rootDir, MEMORY_AUDIT_PATH);
|
|
43874
|
+
const dir = dirname8(filePath);
|
|
43875
|
+
if (!existsSync18(dir)) {
|
|
43876
|
+
await mkdir10(dir, { recursive: true });
|
|
43527
43877
|
}
|
|
43528
|
-
await
|
|
43878
|
+
await writeFile9(filePath, content, "utf-8");
|
|
43529
43879
|
await removeLegacyArtifactIfPresent(rootDir, LEGACY_MEMORY_AUDIT_PATH);
|
|
43530
43880
|
}
|
|
43531
43881
|
async function readMemoryAuditState(rootDir) {
|
|
43532
43882
|
await migrateLegacyArtifactIfNeeded(rootDir, MEMORY_AUDIT_STATE_PATH, LEGACY_MEMORY_AUDIT_STATE_PATH);
|
|
43533
|
-
const filePath =
|
|
43534
|
-
if (!
|
|
43883
|
+
const filePath = join21(rootDir, MEMORY_AUDIT_STATE_PATH);
|
|
43884
|
+
if (!existsSync18(filePath)) {
|
|
43535
43885
|
return null;
|
|
43536
43886
|
}
|
|
43537
43887
|
try {
|
|
43538
|
-
const raw = await
|
|
43888
|
+
const raw = await readFile13(filePath, "utf-8");
|
|
43539
43889
|
const parsed = JSON.parse(raw);
|
|
43540
43890
|
const extraction = isValidExtractionMetadata(parsed.extraction) ? parsed.extraction : void 0;
|
|
43541
43891
|
const pruning = isValidPruneOutcome(parsed.pruning) ? parsed.pruning : void 0;
|
|
@@ -43549,12 +43899,12 @@ async function readMemoryAuditState(rootDir) {
|
|
|
43549
43899
|
}
|
|
43550
43900
|
}
|
|
43551
43901
|
async function writeMemoryAuditState(rootDir, state) {
|
|
43552
|
-
const filePath =
|
|
43553
|
-
const dir =
|
|
43554
|
-
if (!
|
|
43555
|
-
await
|
|
43902
|
+
const filePath = join21(rootDir, MEMORY_AUDIT_STATE_PATH);
|
|
43903
|
+
const dir = dirname8(filePath);
|
|
43904
|
+
if (!existsSync18(dir)) {
|
|
43905
|
+
await mkdir10(dir, { recursive: true });
|
|
43556
43906
|
}
|
|
43557
|
-
await
|
|
43907
|
+
await writeFile9(filePath, JSON.stringify(state, null, 2), "utf-8");
|
|
43558
43908
|
await removeLegacyArtifactIfPresent(rootDir, LEGACY_MEMORY_AUDIT_STATE_PATH);
|
|
43559
43909
|
}
|
|
43560
43910
|
function isValidExtractionMetadata(value) {
|
|
@@ -44028,14 +44378,14 @@ async function generateMemoryAudit(rootDir, lastExtraction, pruningOutcome) {
|
|
|
44028
44378
|
const persistedState = lastExtraction === void 0 || pruningOutcome === void 0 ? await readMemoryAuditState(rootDir) : null;
|
|
44029
44379
|
const effectiveExtraction = lastExtraction ?? persistedState?.extraction;
|
|
44030
44380
|
const effectivePruning = pruningOutcome ?? persistedState?.pruning;
|
|
44031
|
-
const workingMemoryPath =
|
|
44032
|
-
const workingMemoryExists =
|
|
44381
|
+
const workingMemoryPath = join21(rootDir, MEMORY_WORKING_PATH);
|
|
44382
|
+
const workingMemoryExists = existsSync18(workingMemoryPath);
|
|
44033
44383
|
let workingMemorySize = 0;
|
|
44034
44384
|
let workingMemorySectionCount = 0;
|
|
44035
44385
|
let workingMemoryContent = "";
|
|
44036
44386
|
if (workingMemoryExists) {
|
|
44037
44387
|
try {
|
|
44038
|
-
workingMemoryContent = await
|
|
44388
|
+
workingMemoryContent = await readFile13(workingMemoryPath, "utf-8");
|
|
44039
44389
|
workingMemorySize = workingMemoryContent.length;
|
|
44040
44390
|
workingMemorySectionCount = countMarkdownSections(workingMemoryContent);
|
|
44041
44391
|
checks.push({
|
|
@@ -44083,8 +44433,8 @@ async function generateMemoryAudit(rootDir, lastExtraction, pruningOutcome) {
|
|
|
44083
44433
|
details: foundSections.length >= 2 ? `Found sections: ${foundSections.join(", ")}` : `Missing sections: ${requiredSections.filter((s) => !foundSections.includes(s)).join(", ")}`
|
|
44084
44434
|
});
|
|
44085
44435
|
}
|
|
44086
|
-
const insightsMemoryPath =
|
|
44087
|
-
const insightsMemoryExists =
|
|
44436
|
+
const insightsMemoryPath = join21(rootDir, MEMORY_INSIGHTS_PATH);
|
|
44437
|
+
const insightsMemoryExists = existsSync18(insightsMemoryPath);
|
|
44088
44438
|
let insightsMemorySize = 0;
|
|
44089
44439
|
let insightsMemoryContent = "";
|
|
44090
44440
|
const categoryCounts = {
|
|
@@ -44097,7 +44447,7 @@ async function generateMemoryAudit(rootDir, lastExtraction, pruningOutcome) {
|
|
|
44097
44447
|
let lastUpdated;
|
|
44098
44448
|
if (insightsMemoryExists) {
|
|
44099
44449
|
try {
|
|
44100
|
-
insightsMemoryContent = await
|
|
44450
|
+
insightsMemoryContent = await readFile13(insightsMemoryPath, "utf-8");
|
|
44101
44451
|
insightsMemorySize = insightsMemoryContent.length;
|
|
44102
44452
|
for (const [category, header] of Object.entries({
|
|
44103
44453
|
pattern: "## Patterns",
|
|
@@ -46232,7 +46582,7 @@ var require_get_stream = __commonJS({
|
|
|
46232
46582
|
};
|
|
46233
46583
|
const { maxBuffer } = options;
|
|
46234
46584
|
let stream;
|
|
46235
|
-
await new Promise((
|
|
46585
|
+
await new Promise((resolve24, reject) => {
|
|
46236
46586
|
const rejectPromise = (error) => {
|
|
46237
46587
|
if (error && stream.getBufferedLength() <= BufferConstants.MAX_LENGTH) {
|
|
46238
46588
|
error.bufferedData = stream.getBufferedValue();
|
|
@@ -46244,7 +46594,7 @@ var require_get_stream = __commonJS({
|
|
|
46244
46594
|
rejectPromise(error);
|
|
46245
46595
|
return;
|
|
46246
46596
|
}
|
|
46247
|
-
|
|
46597
|
+
resolve24();
|
|
46248
46598
|
});
|
|
46249
46599
|
stream.on("data", () => {
|
|
46250
46600
|
if (stream.getBufferedLength() > maxBuffer) {
|
|
@@ -47538,7 +47888,7 @@ var require_extract_zip = __commonJS({
|
|
|
47538
47888
|
debug("opening", this.zipPath, "with opts", this.opts);
|
|
47539
47889
|
this.zipfile = await openZip(this.zipPath, { lazyEntries: true });
|
|
47540
47890
|
this.canceled = false;
|
|
47541
|
-
return new Promise((
|
|
47891
|
+
return new Promise((resolve24, reject) => {
|
|
47542
47892
|
this.zipfile.on("error", (err) => {
|
|
47543
47893
|
this.canceled = true;
|
|
47544
47894
|
reject(err);
|
|
@@ -47547,7 +47897,7 @@ var require_extract_zip = __commonJS({
|
|
|
47547
47897
|
this.zipfile.on("close", () => {
|
|
47548
47898
|
if (!this.canceled) {
|
|
47549
47899
|
debug("zip extraction complete");
|
|
47550
|
-
|
|
47900
|
+
resolve24();
|
|
47551
47901
|
}
|
|
47552
47902
|
});
|
|
47553
47903
|
this.zipfile.on("entry", async (entry) => {
|
|
@@ -51609,10 +51959,10 @@ var require_resolve_block_map = __commonJS({
|
|
|
51609
51959
|
let offset = bm.offset;
|
|
51610
51960
|
let commentEnd = null;
|
|
51611
51961
|
for (const collItem of bm.items) {
|
|
51612
|
-
const { start, key, sep:
|
|
51962
|
+
const { start, key, sep: sep7, value } = collItem;
|
|
51613
51963
|
const keyProps = resolveProps.resolveProps(start, {
|
|
51614
51964
|
indicator: "explicit-key-ind",
|
|
51615
|
-
next: key ??
|
|
51965
|
+
next: key ?? sep7?.[0],
|
|
51616
51966
|
offset,
|
|
51617
51967
|
onError,
|
|
51618
51968
|
parentIndent: bm.indent,
|
|
@@ -51626,7 +51976,7 @@ var require_resolve_block_map = __commonJS({
|
|
|
51626
51976
|
else if ("indent" in key && key.indent !== bm.indent)
|
|
51627
51977
|
onError(offset, "BAD_INDENT", startColMsg);
|
|
51628
51978
|
}
|
|
51629
|
-
if (!keyProps.anchor && !keyProps.tag && !
|
|
51979
|
+
if (!keyProps.anchor && !keyProps.tag && !sep7) {
|
|
51630
51980
|
commentEnd = keyProps.end;
|
|
51631
51981
|
if (keyProps.comment) {
|
|
51632
51982
|
if (map.comment)
|
|
@@ -51650,7 +52000,7 @@ var require_resolve_block_map = __commonJS({
|
|
|
51650
52000
|
ctx.atKey = false;
|
|
51651
52001
|
if (utilMapIncludes.mapIncludes(ctx, map.items, keyNode))
|
|
51652
52002
|
onError(keyStart, "DUPLICATE_KEY", "Map keys must be unique");
|
|
51653
|
-
const valueProps = resolveProps.resolveProps(
|
|
52003
|
+
const valueProps = resolveProps.resolveProps(sep7 ?? [], {
|
|
51654
52004
|
indicator: "map-value-ind",
|
|
51655
52005
|
next: value,
|
|
51656
52006
|
offset: keyNode.range[2],
|
|
@@ -51666,7 +52016,7 @@ var require_resolve_block_map = __commonJS({
|
|
|
51666
52016
|
if (ctx.options.strict && keyProps.start < valueProps.found.offset - 1024)
|
|
51667
52017
|
onError(keyNode.range, "KEY_OVER_1024_CHARS", "The : indicator must be at most 1024 chars after the start of an implicit block mapping key");
|
|
51668
52018
|
}
|
|
51669
|
-
const valueNode = value ? composeNode(ctx, value, valueProps, onError) : composeEmptyNode(ctx, offset,
|
|
52019
|
+
const valueNode = value ? composeNode(ctx, value, valueProps, onError) : composeEmptyNode(ctx, offset, sep7, null, valueProps, onError);
|
|
51670
52020
|
if (ctx.schema.compat)
|
|
51671
52021
|
utilFlowIndentCheck.flowIndentCheck(bm.indent, value, onError);
|
|
51672
52022
|
offset = valueNode.range[2];
|
|
@@ -51757,7 +52107,7 @@ var require_resolve_end = __commonJS({
|
|
|
51757
52107
|
let comment = "";
|
|
51758
52108
|
if (end) {
|
|
51759
52109
|
let hasSpace = false;
|
|
51760
|
-
let
|
|
52110
|
+
let sep7 = "";
|
|
51761
52111
|
for (const token of end) {
|
|
51762
52112
|
const { source, type } = token;
|
|
51763
52113
|
switch (type) {
|
|
@@ -51771,13 +52121,13 @@ var require_resolve_end = __commonJS({
|
|
|
51771
52121
|
if (!comment)
|
|
51772
52122
|
comment = cb;
|
|
51773
52123
|
else
|
|
51774
|
-
comment +=
|
|
51775
|
-
|
|
52124
|
+
comment += sep7 + cb;
|
|
52125
|
+
sep7 = "";
|
|
51776
52126
|
break;
|
|
51777
52127
|
}
|
|
51778
52128
|
case "newline":
|
|
51779
52129
|
if (comment)
|
|
51780
|
-
|
|
52130
|
+
sep7 += source;
|
|
51781
52131
|
hasSpace = true;
|
|
51782
52132
|
break;
|
|
51783
52133
|
default:
|
|
@@ -51820,18 +52170,18 @@ var require_resolve_flow_collection = __commonJS({
|
|
|
51820
52170
|
let offset = fc.offset + fc.start.source.length;
|
|
51821
52171
|
for (let i = 0; i < fc.items.length; ++i) {
|
|
51822
52172
|
const collItem = fc.items[i];
|
|
51823
|
-
const { start, key, sep:
|
|
52173
|
+
const { start, key, sep: sep7, value } = collItem;
|
|
51824
52174
|
const props = resolveProps.resolveProps(start, {
|
|
51825
52175
|
flow: fcName,
|
|
51826
52176
|
indicator: "explicit-key-ind",
|
|
51827
|
-
next: key ??
|
|
52177
|
+
next: key ?? sep7?.[0],
|
|
51828
52178
|
offset,
|
|
51829
52179
|
onError,
|
|
51830
52180
|
parentIndent: fc.indent,
|
|
51831
52181
|
startOnNewline: false
|
|
51832
52182
|
});
|
|
51833
52183
|
if (!props.found) {
|
|
51834
|
-
if (!props.anchor && !props.tag && !
|
|
52184
|
+
if (!props.anchor && !props.tag && !sep7 && !value) {
|
|
51835
52185
|
if (i === 0 && props.comma)
|
|
51836
52186
|
onError(props.comma, "UNEXPECTED_TOKEN", `Unexpected , in ${fcName}`);
|
|
51837
52187
|
else if (i < fc.items.length - 1)
|
|
@@ -51885,8 +52235,8 @@ var require_resolve_flow_collection = __commonJS({
|
|
|
51885
52235
|
}
|
|
51886
52236
|
}
|
|
51887
52237
|
}
|
|
51888
|
-
if (!isMap && !
|
|
51889
|
-
const valueNode = value ? composeNode(ctx, value, props, onError) : composeEmptyNode(ctx, props.end,
|
|
52238
|
+
if (!isMap && !sep7 && !props.found) {
|
|
52239
|
+
const valueNode = value ? composeNode(ctx, value, props, onError) : composeEmptyNode(ctx, props.end, sep7, null, props, onError);
|
|
51890
52240
|
coll.items.push(valueNode);
|
|
51891
52241
|
offset = valueNode.range[2];
|
|
51892
52242
|
if (isBlock(value))
|
|
@@ -51898,7 +52248,7 @@ var require_resolve_flow_collection = __commonJS({
|
|
|
51898
52248
|
if (isBlock(key))
|
|
51899
52249
|
onError(keyNode.range, "BLOCK_IN_FLOW", blockMsg);
|
|
51900
52250
|
ctx.atKey = false;
|
|
51901
|
-
const valueProps = resolveProps.resolveProps(
|
|
52251
|
+
const valueProps = resolveProps.resolveProps(sep7 ?? [], {
|
|
51902
52252
|
flow: fcName,
|
|
51903
52253
|
indicator: "map-value-ind",
|
|
51904
52254
|
next: value,
|
|
@@ -51909,8 +52259,8 @@ var require_resolve_flow_collection = __commonJS({
|
|
|
51909
52259
|
});
|
|
51910
52260
|
if (valueProps.found) {
|
|
51911
52261
|
if (!isMap && !props.found && ctx.options.strict) {
|
|
51912
|
-
if (
|
|
51913
|
-
for (const st of
|
|
52262
|
+
if (sep7)
|
|
52263
|
+
for (const st of sep7) {
|
|
51914
52264
|
if (st === valueProps.found)
|
|
51915
52265
|
break;
|
|
51916
52266
|
if (st.type === "newline") {
|
|
@@ -51927,7 +52277,7 @@ var require_resolve_flow_collection = __commonJS({
|
|
|
51927
52277
|
else
|
|
51928
52278
|
onError(valueProps.start, "MISSING_CHAR", `Missing , or : between ${fcName} items`);
|
|
51929
52279
|
}
|
|
51930
|
-
const valueNode = value ? composeNode(ctx, value, valueProps, onError) : valueProps.found ? composeEmptyNode(ctx, valueProps.end,
|
|
52280
|
+
const valueNode = value ? composeNode(ctx, value, valueProps, onError) : valueProps.found ? composeEmptyNode(ctx, valueProps.end, sep7, null, valueProps, onError) : null;
|
|
51931
52281
|
if (valueNode) {
|
|
51932
52282
|
if (isBlock(value))
|
|
51933
52283
|
onError(valueNode.range, "BLOCK_IN_FLOW", blockMsg);
|
|
@@ -52107,7 +52457,7 @@ var require_resolve_block_scalar = __commonJS({
|
|
|
52107
52457
|
chompStart = i + 1;
|
|
52108
52458
|
}
|
|
52109
52459
|
let value = "";
|
|
52110
|
-
let
|
|
52460
|
+
let sep7 = "";
|
|
52111
52461
|
let prevMoreIndented = false;
|
|
52112
52462
|
for (let i = 0; i < contentStart; ++i)
|
|
52113
52463
|
value += lines[i][0].slice(trimIndent) + "\n";
|
|
@@ -52124,24 +52474,24 @@ var require_resolve_block_scalar = __commonJS({
|
|
|
52124
52474
|
indent = "";
|
|
52125
52475
|
}
|
|
52126
52476
|
if (type === Scalar.Scalar.BLOCK_LITERAL) {
|
|
52127
|
-
value +=
|
|
52128
|
-
|
|
52477
|
+
value += sep7 + indent.slice(trimIndent) + content;
|
|
52478
|
+
sep7 = "\n";
|
|
52129
52479
|
} else if (indent.length > trimIndent || content[0] === " ") {
|
|
52130
|
-
if (
|
|
52131
|
-
|
|
52132
|
-
else if (!prevMoreIndented &&
|
|
52133
|
-
|
|
52134
|
-
value +=
|
|
52135
|
-
|
|
52480
|
+
if (sep7 === " ")
|
|
52481
|
+
sep7 = "\n";
|
|
52482
|
+
else if (!prevMoreIndented && sep7 === "\n")
|
|
52483
|
+
sep7 = "\n\n";
|
|
52484
|
+
value += sep7 + indent.slice(trimIndent) + content;
|
|
52485
|
+
sep7 = "\n";
|
|
52136
52486
|
prevMoreIndented = true;
|
|
52137
52487
|
} else if (content === "") {
|
|
52138
|
-
if (
|
|
52488
|
+
if (sep7 === "\n")
|
|
52139
52489
|
value += "\n";
|
|
52140
52490
|
else
|
|
52141
|
-
|
|
52491
|
+
sep7 = "\n";
|
|
52142
52492
|
} else {
|
|
52143
|
-
value +=
|
|
52144
|
-
|
|
52493
|
+
value += sep7 + content;
|
|
52494
|
+
sep7 = " ";
|
|
52145
52495
|
prevMoreIndented = false;
|
|
52146
52496
|
}
|
|
52147
52497
|
}
|
|
@@ -52323,25 +52673,25 @@ var require_resolve_flow_scalar = __commonJS({
|
|
|
52323
52673
|
if (!match)
|
|
52324
52674
|
return source;
|
|
52325
52675
|
let res = match[1];
|
|
52326
|
-
let
|
|
52676
|
+
let sep7 = " ";
|
|
52327
52677
|
let pos = first.lastIndex;
|
|
52328
52678
|
line.lastIndex = pos;
|
|
52329
52679
|
while (match = line.exec(source)) {
|
|
52330
52680
|
if (match[1] === "") {
|
|
52331
|
-
if (
|
|
52332
|
-
res +=
|
|
52681
|
+
if (sep7 === "\n")
|
|
52682
|
+
res += sep7;
|
|
52333
52683
|
else
|
|
52334
|
-
|
|
52684
|
+
sep7 = "\n";
|
|
52335
52685
|
} else {
|
|
52336
|
-
res +=
|
|
52337
|
-
|
|
52686
|
+
res += sep7 + match[1];
|
|
52687
|
+
sep7 = " ";
|
|
52338
52688
|
}
|
|
52339
52689
|
pos = line.lastIndex;
|
|
52340
52690
|
}
|
|
52341
52691
|
const last = /[ \t]*(.*)/sy;
|
|
52342
52692
|
last.lastIndex = pos;
|
|
52343
52693
|
match = last.exec(source);
|
|
52344
|
-
return res +
|
|
52694
|
+
return res + sep7 + (match?.[1] ?? "");
|
|
52345
52695
|
}
|
|
52346
52696
|
function doubleQuotedValue(source, onError) {
|
|
52347
52697
|
let res = "";
|
|
@@ -53148,14 +53498,14 @@ var require_cst_stringify = __commonJS({
|
|
|
53148
53498
|
}
|
|
53149
53499
|
}
|
|
53150
53500
|
}
|
|
53151
|
-
function stringifyItem({ start, key, sep:
|
|
53501
|
+
function stringifyItem({ start, key, sep: sep7, value }) {
|
|
53152
53502
|
let res = "";
|
|
53153
53503
|
for (const st of start)
|
|
53154
53504
|
res += st.source;
|
|
53155
53505
|
if (key)
|
|
53156
53506
|
res += stringifyToken(key);
|
|
53157
|
-
if (
|
|
53158
|
-
for (const st of
|
|
53507
|
+
if (sep7)
|
|
53508
|
+
for (const st of sep7)
|
|
53159
53509
|
res += st.source;
|
|
53160
53510
|
if (value)
|
|
53161
53511
|
res += stringifyToken(value);
|
|
@@ -54305,18 +54655,18 @@ var require_parser = __commonJS({
|
|
|
54305
54655
|
if (this.type === "map-value-ind") {
|
|
54306
54656
|
const prev = getPrevProps(this.peek(2));
|
|
54307
54657
|
const start = getFirstKeyStartProps(prev);
|
|
54308
|
-
let
|
|
54658
|
+
let sep7;
|
|
54309
54659
|
if (scalar.end) {
|
|
54310
|
-
|
|
54311
|
-
|
|
54660
|
+
sep7 = scalar.end;
|
|
54661
|
+
sep7.push(this.sourceToken);
|
|
54312
54662
|
delete scalar.end;
|
|
54313
54663
|
} else
|
|
54314
|
-
|
|
54664
|
+
sep7 = [this.sourceToken];
|
|
54315
54665
|
const map = {
|
|
54316
54666
|
type: "block-map",
|
|
54317
54667
|
offset: scalar.offset,
|
|
54318
54668
|
indent: scalar.indent,
|
|
54319
|
-
items: [{ start, key: scalar, sep:
|
|
54669
|
+
items: [{ start, key: scalar, sep: sep7 }]
|
|
54320
54670
|
};
|
|
54321
54671
|
this.onKeyLine = true;
|
|
54322
54672
|
this.stack[this.stack.length - 1] = map;
|
|
@@ -54469,15 +54819,15 @@ var require_parser = __commonJS({
|
|
|
54469
54819
|
} else if (isFlowToken(it.key) && !includesToken(it.sep, "newline")) {
|
|
54470
54820
|
const start2 = getFirstKeyStartProps(it.start);
|
|
54471
54821
|
const key = it.key;
|
|
54472
|
-
const
|
|
54473
|
-
|
|
54822
|
+
const sep7 = it.sep;
|
|
54823
|
+
sep7.push(this.sourceToken);
|
|
54474
54824
|
delete it.key;
|
|
54475
54825
|
delete it.sep;
|
|
54476
54826
|
this.stack.push({
|
|
54477
54827
|
type: "block-map",
|
|
54478
54828
|
offset: this.offset,
|
|
54479
54829
|
indent: this.indent,
|
|
54480
|
-
items: [{ start: start2, key, sep:
|
|
54830
|
+
items: [{ start: start2, key, sep: sep7 }]
|
|
54481
54831
|
});
|
|
54482
54832
|
} else if (start.length > 0) {
|
|
54483
54833
|
it.sep = it.sep.concat(start, this.sourceToken);
|
|
@@ -54671,13 +55021,13 @@ var require_parser = __commonJS({
|
|
|
54671
55021
|
const prev = getPrevProps(parent);
|
|
54672
55022
|
const start = getFirstKeyStartProps(prev);
|
|
54673
55023
|
fixFlowSeqItems(fc);
|
|
54674
|
-
const
|
|
54675
|
-
|
|
55024
|
+
const sep7 = fc.end.splice(1, fc.end.length);
|
|
55025
|
+
sep7.push(this.sourceToken);
|
|
54676
55026
|
const map = {
|
|
54677
55027
|
type: "block-map",
|
|
54678
55028
|
offset: fc.offset,
|
|
54679
55029
|
indent: fc.indent,
|
|
54680
|
-
items: [{ start, key: fc, sep:
|
|
55030
|
+
items: [{ start, key: fc, sep: sep7 }]
|
|
54681
55031
|
};
|
|
54682
55032
|
this.onKeyLine = true;
|
|
54683
55033
|
this.stack[this.stack.length - 1] = map;
|
|
@@ -54956,9 +55306,9 @@ var require_dist3 = __commonJS({
|
|
|
54956
55306
|
});
|
|
54957
55307
|
|
|
54958
55308
|
// ../core/src/agent-companies-parser.ts
|
|
54959
|
-
import { existsSync as
|
|
55309
|
+
import { existsSync as existsSync19, mkdtempSync, readdirSync as readdirSync2, readFileSync as readFileSync5, rmSync, statSync as statSync4 } from "node:fs";
|
|
54960
55310
|
import { tmpdir as tmpdir3 } from "node:os";
|
|
54961
|
-
import { join as
|
|
55311
|
+
import { isAbsolute as isAbsolute7, join as join22, normalize as normalize3, resolve as resolve11 } from "node:path";
|
|
54962
55312
|
function slugifyAgentReference(value) {
|
|
54963
55313
|
return value.trim().toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/-+/g, "-").replace(/^-+|-+$/g, "");
|
|
54964
55314
|
}
|
|
@@ -55183,15 +55533,15 @@ function parseManifestFile(filePath, parser) {
|
|
|
55183
55533
|
}
|
|
55184
55534
|
}
|
|
55185
55535
|
function parseManifestSubdirectories(rootDir, sectionDir, filename, parser) {
|
|
55186
|
-
const sectionPath =
|
|
55187
|
-
if (!
|
|
55536
|
+
const sectionPath = join22(rootDir, sectionDir);
|
|
55537
|
+
if (!existsSync19(sectionPath)) {
|
|
55188
55538
|
return [];
|
|
55189
55539
|
}
|
|
55190
55540
|
const manifests = [];
|
|
55191
55541
|
const entries = readdirSync2(sectionPath, { withFileTypes: true }).filter((entry) => entry.isDirectory()).sort((a, b) => a.name.localeCompare(b.name));
|
|
55192
55542
|
for (const entry of entries) {
|
|
55193
|
-
const manifestPath =
|
|
55194
|
-
if (!
|
|
55543
|
+
const manifestPath = join22(sectionPath, entry.name, filename);
|
|
55544
|
+
if (!existsSync19(manifestPath)) {
|
|
55195
55545
|
continue;
|
|
55196
55546
|
}
|
|
55197
55547
|
manifests.push(parseManifestFile(manifestPath, parser));
|
|
@@ -55228,18 +55578,18 @@ function walkTeamIncludes(teams) {
|
|
|
55228
55578
|
}
|
|
55229
55579
|
}
|
|
55230
55580
|
function parseCompanyDirectory(dirPath) {
|
|
55231
|
-
const resolvedPath =
|
|
55232
|
-
if (!
|
|
55581
|
+
const resolvedPath = resolve11(dirPath);
|
|
55582
|
+
if (!existsSync19(resolvedPath)) {
|
|
55233
55583
|
throw new AgentCompaniesParseError(`Company directory does not exist: ${resolvedPath}`);
|
|
55234
55584
|
}
|
|
55235
55585
|
if (!statSync4(resolvedPath).isDirectory()) {
|
|
55236
55586
|
throw new AgentCompaniesParseError(`Company path is not a directory: ${resolvedPath}`);
|
|
55237
55587
|
}
|
|
55238
|
-
const companyPath =
|
|
55588
|
+
const companyPath = join22(resolvedPath, "COMPANY.md");
|
|
55239
55589
|
const teams = parseManifestSubdirectories(resolvedPath, "teams", "TEAM.md", parseTeamManifest);
|
|
55240
55590
|
walkTeamIncludes(teams);
|
|
55241
55591
|
return {
|
|
55242
|
-
company:
|
|
55592
|
+
company: existsSync19(companyPath) ? parseManifestFile(companyPath, parseCompanyManifest) : void 0,
|
|
55243
55593
|
agents: parseManifestSubdirectories(resolvedPath, "agents", "AGENTS.md", parseAgentManifest),
|
|
55244
55594
|
teams,
|
|
55245
55595
|
projects: parseManifestSubdirectories(
|
|
@@ -55253,20 +55603,20 @@ function parseCompanyDirectory(dirPath) {
|
|
|
55253
55603
|
};
|
|
55254
55604
|
}
|
|
55255
55605
|
function resolveExtractionRoot(tempDir) {
|
|
55256
|
-
if (
|
|
55606
|
+
if (existsSync19(join22(tempDir, "COMPANY.md"))) {
|
|
55257
55607
|
return tempDir;
|
|
55258
55608
|
}
|
|
55259
55609
|
const directories = readdirSync2(tempDir, { withFileTypes: true }).filter(
|
|
55260
55610
|
(entry) => entry.isDirectory()
|
|
55261
55611
|
);
|
|
55262
55612
|
for (const directory of directories) {
|
|
55263
|
-
const candidate =
|
|
55264
|
-
if (
|
|
55613
|
+
const candidate = join22(tempDir, directory.name);
|
|
55614
|
+
if (existsSync19(join22(candidate, "COMPANY.md"))) {
|
|
55265
55615
|
return candidate;
|
|
55266
55616
|
}
|
|
55267
55617
|
}
|
|
55268
55618
|
if (directories.length === 1) {
|
|
55269
|
-
return resolveExtractionRoot(
|
|
55619
|
+
return resolveExtractionRoot(join22(tempDir, directories[0].name));
|
|
55270
55620
|
}
|
|
55271
55621
|
return tempDir;
|
|
55272
55622
|
}
|
|
@@ -55278,9 +55628,35 @@ async function extractTarArchive(archivePath, outputDir) {
|
|
|
55278
55628
|
const execFileAsync6 = promisify14(execFile8);
|
|
55279
55629
|
await execFileAsync6("tar", ["xzf", archivePath, "-C", outputDir]);
|
|
55280
55630
|
}
|
|
55281
|
-
|
|
55282
|
-
const
|
|
55283
|
-
|
|
55631
|
+
function sanitizeCompanySubPath(subPath) {
|
|
55632
|
+
const trimmed = subPath.trim();
|
|
55633
|
+
if (trimmed.length === 0) {
|
|
55634
|
+
throw new AgentCompaniesParseError("subPath must not be empty");
|
|
55635
|
+
}
|
|
55636
|
+
if (trimmed.includes("\\")) {
|
|
55637
|
+
throw new AgentCompaniesParseError(`Invalid subPath "${subPath}": backslashes are not allowed`);
|
|
55638
|
+
}
|
|
55639
|
+
if (isAbsolute7(trimmed)) {
|
|
55640
|
+
throw new AgentCompaniesParseError(`Invalid subPath "${subPath}": absolute paths are not allowed`);
|
|
55641
|
+
}
|
|
55642
|
+
const normalized = normalize3(trimmed).replace(/^\/+/, "");
|
|
55643
|
+
if (normalized === "" || normalized === "." || normalized.split("/").some((segment) => segment === "..")) {
|
|
55644
|
+
throw new AgentCompaniesParseError(`Invalid subPath "${subPath}": path traversal is not allowed`);
|
|
55645
|
+
}
|
|
55646
|
+
return normalized;
|
|
55647
|
+
}
|
|
55648
|
+
function findArchiveWrapperRoot(tempDir) {
|
|
55649
|
+
const directories = readdirSync2(tempDir, { withFileTypes: true }).filter(
|
|
55650
|
+
(entry) => entry.isDirectory()
|
|
55651
|
+
);
|
|
55652
|
+
if (directories.length === 1) {
|
|
55653
|
+
return join22(tempDir, directories[0].name);
|
|
55654
|
+
}
|
|
55655
|
+
return tempDir;
|
|
55656
|
+
}
|
|
55657
|
+
async function parseCompanyArchive(archivePath, options) {
|
|
55658
|
+
const resolvedArchivePath = resolve11(archivePath);
|
|
55659
|
+
const tempDir = mkdtempSync(join22(tmpdir3(), "agent-companies-"));
|
|
55284
55660
|
try {
|
|
55285
55661
|
if (resolvedArchivePath.endsWith(".tar.gz") || resolvedArchivePath.endsWith(".tgz")) {
|
|
55286
55662
|
await extractTarArchive(resolvedArchivePath, tempDir);
|
|
@@ -55291,6 +55667,18 @@ async function parseCompanyArchive(archivePath) {
|
|
|
55291
55667
|
"Unsupported archive format. Expected .tar.gz, .tgz, or .zip"
|
|
55292
55668
|
);
|
|
55293
55669
|
}
|
|
55670
|
+
if (typeof options?.subPath === "string") {
|
|
55671
|
+
const wrapperRoot = findArchiveWrapperRoot(tempDir);
|
|
55672
|
+
const sanitizedSubPath = sanitizeCompanySubPath(options.subPath);
|
|
55673
|
+
const candidateRoot = join22(wrapperRoot, sanitizedSubPath);
|
|
55674
|
+
const companyManifestPath = join22(candidateRoot, "COMPANY.md");
|
|
55675
|
+
if (!existsSync19(companyManifestPath)) {
|
|
55676
|
+
throw new AgentCompaniesParseError(
|
|
55677
|
+
`Company manifest not found at archive subPath "${sanitizedSubPath}" (expected ${companyManifestPath})`
|
|
55678
|
+
);
|
|
55679
|
+
}
|
|
55680
|
+
return parseCompanyDirectory(candidateRoot);
|
|
55681
|
+
}
|
|
55294
55682
|
return parseCompanyDirectory(resolveExtractionRoot(tempDir));
|
|
55295
55683
|
} catch (error) {
|
|
55296
55684
|
if (error instanceof AgentCompaniesParseError) {
|
|
@@ -55469,8 +55857,8 @@ var init_agent_companies_parser = __esm({
|
|
|
55469
55857
|
});
|
|
55470
55858
|
|
|
55471
55859
|
// ../core/src/agent-companies-exporter.ts
|
|
55472
|
-
import { mkdir as
|
|
55473
|
-
import { resolve as
|
|
55860
|
+
import { mkdir as mkdir11, writeFile as writeFile10 } from "node:fs/promises";
|
|
55861
|
+
import { resolve as resolve12, join as join23 } from "node:path";
|
|
55474
55862
|
function trimToUndefined(value) {
|
|
55475
55863
|
if (typeof value !== "string") {
|
|
55476
55864
|
return void 0;
|
|
@@ -55578,7 +55966,7 @@ function generateSkillMd(skillName) {
|
|
|
55578
55966
|
);
|
|
55579
55967
|
}
|
|
55580
55968
|
async function exportAgentsToDirectory(agents, outputDir, options) {
|
|
55581
|
-
const resolvedOutputDir =
|
|
55969
|
+
const resolvedOutputDir = resolve12(outputDir);
|
|
55582
55970
|
const includeSkills = options?.includeSkills ?? true;
|
|
55583
55971
|
const result = {
|
|
55584
55972
|
outputDir: resolvedOutputDir,
|
|
@@ -55587,15 +55975,15 @@ async function exportAgentsToDirectory(agents, outputDir, options) {
|
|
|
55587
55975
|
filesWritten: [],
|
|
55588
55976
|
errors: []
|
|
55589
55977
|
};
|
|
55590
|
-
await
|
|
55591
|
-
await
|
|
55592
|
-
const companyMdPath =
|
|
55978
|
+
await mkdir11(resolvedOutputDir, { recursive: true });
|
|
55979
|
+
await mkdir11(join23(resolvedOutputDir, "agents"), { recursive: true });
|
|
55980
|
+
const companyMdPath = join23(resolvedOutputDir, "COMPANY.md");
|
|
55593
55981
|
const companyMd = generateCompanyMd(agents, {
|
|
55594
55982
|
name: options?.companyName,
|
|
55595
55983
|
description: options?.companyDescription,
|
|
55596
55984
|
slug: options?.companySlug
|
|
55597
55985
|
});
|
|
55598
|
-
await
|
|
55986
|
+
await writeFile10(companyMdPath, companyMd, "utf-8");
|
|
55599
55987
|
result.filesWritten.push(companyMdPath);
|
|
55600
55988
|
const validAgents = agents.filter((agent) => {
|
|
55601
55989
|
if (!trimToUndefined(agent.name)) {
|
|
@@ -55641,9 +56029,9 @@ async function exportAgentsToDirectory(agents, outputDir, options) {
|
|
|
55641
56029
|
skills: skillRefs
|
|
55642
56030
|
});
|
|
55643
56031
|
try {
|
|
55644
|
-
const agentDir =
|
|
55645
|
-
const agentMdPath =
|
|
55646
|
-
await
|
|
56032
|
+
const agentDir = join23(resolvedOutputDir, "agents", agentSlug);
|
|
56033
|
+
const agentMdPath = join23(agentDir, "AGENTS.md");
|
|
56034
|
+
await mkdir11(agentDir, { recursive: true });
|
|
55647
56035
|
const frontmatter = {
|
|
55648
56036
|
name: manifest.name,
|
|
55649
56037
|
title: manifest.title,
|
|
@@ -55656,7 +56044,7 @@ async function exportAgentsToDirectory(agents, outputDir, options) {
|
|
|
55656
56044
|
memory: manifest.memory
|
|
55657
56045
|
};
|
|
55658
56046
|
const content = toFrontmatterMarkdown(frontmatter, manifest.instructionBody ?? "");
|
|
55659
|
-
await
|
|
56047
|
+
await writeFile10(agentMdPath, content, "utf-8");
|
|
55660
56048
|
result.agentsExported += 1;
|
|
55661
56049
|
result.filesWritten.push(agentMdPath);
|
|
55662
56050
|
} catch (error) {
|
|
@@ -55667,13 +56055,13 @@ async function exportAgentsToDirectory(agents, outputDir, options) {
|
|
|
55667
56055
|
}
|
|
55668
56056
|
}
|
|
55669
56057
|
if (includeSkills && skillByName.size > 0) {
|
|
55670
|
-
const skillsDir =
|
|
55671
|
-
await
|
|
56058
|
+
const skillsDir = join23(resolvedOutputDir, "skills");
|
|
56059
|
+
await mkdir11(skillsDir, { recursive: true });
|
|
55672
56060
|
for (const skill of skillByName.values()) {
|
|
55673
|
-
const skillDir =
|
|
55674
|
-
const skillPath =
|
|
55675
|
-
await
|
|
55676
|
-
await
|
|
56061
|
+
const skillDir = join23(skillsDir, skill.slug);
|
|
56062
|
+
const skillPath = join23(skillDir, "SKILL.md");
|
|
56063
|
+
await mkdir11(skillDir, { recursive: true });
|
|
56064
|
+
await writeFile10(skillPath, generateSkillMd(skill.name), "utf-8");
|
|
55677
56065
|
result.skillsExported += 1;
|
|
55678
56066
|
result.filesWritten.push(skillPath);
|
|
55679
56067
|
}
|
|
@@ -56401,19 +56789,19 @@ var init_chat_store = __esm({
|
|
|
56401
56789
|
});
|
|
56402
56790
|
|
|
56403
56791
|
// ../core/src/oauth-credential-interop.ts
|
|
56404
|
-
import { existsSync as
|
|
56792
|
+
import { existsSync as existsSync20, readFileSync as readFileSync6 } from "node:fs";
|
|
56405
56793
|
import { homedir as homedir4 } from "node:os";
|
|
56406
|
-
import { join as
|
|
56794
|
+
import { join as join24 } from "node:path";
|
|
56407
56795
|
function getHomeDir4() {
|
|
56408
56796
|
return process.env.HOME || process.env.USERPROFILE || homedir4();
|
|
56409
56797
|
}
|
|
56410
56798
|
function getCodexCliAuthPath(home = getHomeDir4()) {
|
|
56411
|
-
return
|
|
56799
|
+
return join24(home, ".codex", "auth.json");
|
|
56412
56800
|
}
|
|
56413
56801
|
function getClaudeCodeCredentialPaths(home = getHomeDir4()) {
|
|
56414
56802
|
return [
|
|
56415
|
-
|
|
56416
|
-
|
|
56803
|
+
join24(home, ".claude", ".credentials.json"),
|
|
56804
|
+
join24(home, ".config", "claude", ".credentials.json")
|
|
56417
56805
|
];
|
|
56418
56806
|
}
|
|
56419
56807
|
function parseJwtPayload(token) {
|
|
@@ -56580,7 +56968,7 @@ function extractClaudeCliStoredCredential(raw) {
|
|
|
56580
56968
|
};
|
|
56581
56969
|
}
|
|
56582
56970
|
function readStoredCredentialsFromAuthFile(authPath) {
|
|
56583
|
-
if (!
|
|
56971
|
+
if (!existsSync20(authPath)) {
|
|
56584
56972
|
return {};
|
|
56585
56973
|
}
|
|
56586
56974
|
try {
|
|
@@ -56717,6 +57105,7 @@ __export(src_exports, {
|
|
|
56717
57105
|
MAX_TITLE_LENGTH: () => MAX_TITLE_LENGTH,
|
|
56718
57106
|
MEMORY_AUDIT_PATH: () => MEMORY_AUDIT_PATH,
|
|
56719
57107
|
MEMORY_BACKEND_SETTINGS_KEYS: () => MEMORY_BACKEND_SETTINGS_KEYS,
|
|
57108
|
+
MEMORY_BACKUP_SCHEDULE_NAME: () => MEMORY_BACKUP_SCHEDULE_NAME,
|
|
56720
57109
|
MEMORY_DREAMS_FILENAME: () => MEMORY_DREAMS_FILENAME,
|
|
56721
57110
|
MEMORY_DREAMS_SCHEDULE_NAME: () => MEMORY_DREAMS_SCHEDULE_NAME,
|
|
56722
57111
|
MEMORY_INSIGHTS_PATH: () => MEMORY_INSIGHTS_PATH,
|
|
@@ -56735,6 +57124,7 @@ __export(src_exports, {
|
|
|
56735
57124
|
MISSION_EVENT_TYPES: () => MISSION_EVENT_TYPES,
|
|
56736
57125
|
MISSION_STATUSES: () => MISSION_STATUSES,
|
|
56737
57126
|
MemoryBackendError: () => MemoryBackendError,
|
|
57127
|
+
MemoryBackupManager: () => MemoryBackupManager,
|
|
56738
57128
|
MeshConfigGenerator: () => MeshConfigGenerator,
|
|
56739
57129
|
MessageStore: () => MessageStore,
|
|
56740
57130
|
MigrationCoordinator: () => MigrationCoordinator,
|
|
@@ -56840,6 +57230,7 @@ __export(src_exports, {
|
|
|
56840
57230
|
createDistributedTaskIdAllocator: () => createDistributedTaskIdAllocator,
|
|
56841
57231
|
createFenceToken: () => createFenceToken,
|
|
56842
57232
|
createInsightExtractionAutomation: () => createInsightExtractionAutomation,
|
|
57233
|
+
createMemoryBackupManager: () => createMemoryBackupManager,
|
|
56843
57234
|
createMemoryDreamsAutomation: () => createMemoryDreamsAutomation,
|
|
56844
57235
|
createMissionHierarchySnapshot: () => createMissionHierarchySnapshot,
|
|
56845
57236
|
createProjectSettingsSnapshot: () => createProjectSettingsSnapshot,
|
|
@@ -57029,6 +57420,7 @@ __export(src_exports, {
|
|
|
57029
57420
|
runGhAsync: () => runGhAsync,
|
|
57030
57421
|
runGhJson: () => runGhJson,
|
|
57031
57422
|
runGhJsonAsync: () => runGhJsonAsync,
|
|
57423
|
+
runMemoryBackupCommand: () => runMemoryBackupCommand,
|
|
57032
57424
|
runScheduledEvalBatch: () => runScheduledEvalBatch,
|
|
57033
57425
|
sanitizeCommitSubject: () => sanitizeCommitSubject,
|
|
57034
57426
|
sanitizeDockerNodeConfigForResponse: () => sanitizeDockerNodeConfigForResponse,
|
|
@@ -57052,6 +57444,8 @@ __export(src_exports, {
|
|
|
57052
57444
|
syncBackupAutomation: () => syncBackupAutomation,
|
|
57053
57445
|
syncBackupRoutine: () => syncBackupRoutine,
|
|
57054
57446
|
syncInsightExtractionAutomation: () => syncInsightExtractionAutomation,
|
|
57447
|
+
syncMemoryBackupAutomation: () => syncMemoryBackupAutomation,
|
|
57448
|
+
syncMemoryBackupRoutine: () => syncMemoryBackupRoutine,
|
|
57055
57449
|
syncMemoryDreamsAutomation: () => syncMemoryDreamsAutomation,
|
|
57056
57450
|
syncScheduledEvalBatchAutomation: () => syncScheduledEvalBatchAutomation,
|
|
57057
57451
|
taskMatchesReplicatedCreate: () => taskMatchesReplicatedCreate,
|
|
@@ -57066,6 +57460,7 @@ __export(src_exports, {
|
|
|
57066
57460
|
validateDescription: () => validateDescription,
|
|
57067
57461
|
validateDockerNodeConfig: () => validateDockerNodeConfig,
|
|
57068
57462
|
validateImportData: () => validateImportData,
|
|
57463
|
+
validateMemoryBackupSchedule: () => validateMemoryBackupSchedule,
|
|
57069
57464
|
validateMessageMetadata: () => validateMessageMetadata,
|
|
57070
57465
|
validateNodeOverrideChange: () => validateNodeOverrideChange,
|
|
57071
57466
|
validatePluginManifest: () => validatePluginManifest,
|
|
@@ -57123,6 +57518,7 @@ var init_src = __esm({
|
|
|
57123
57518
|
init_plugin_loader();
|
|
57124
57519
|
init_plugin_security_scan();
|
|
57125
57520
|
init_backup();
|
|
57521
|
+
init_memory_backup();
|
|
57126
57522
|
init_settings_export();
|
|
57127
57523
|
init_ai_summarize();
|
|
57128
57524
|
init_model_resolution();
|
|
@@ -57876,12 +58272,12 @@ var init_concurrency = __esm({
|
|
|
57876
58272
|
this._active++;
|
|
57877
58273
|
return Promise.resolve();
|
|
57878
58274
|
}
|
|
57879
|
-
return new Promise((
|
|
58275
|
+
return new Promise((resolve24) => {
|
|
57880
58276
|
this._waiters.push({
|
|
57881
58277
|
priority,
|
|
57882
58278
|
resolve: () => {
|
|
57883
58279
|
this._active++;
|
|
57884
|
-
|
|
58280
|
+
resolve24();
|
|
57885
58281
|
}
|
|
57886
58282
|
});
|
|
57887
58283
|
});
|
|
@@ -58689,23 +59085,23 @@ var init_github_provider = __esm({
|
|
|
58689
59085
|
});
|
|
58690
59086
|
|
|
58691
59087
|
// ../engine/src/skill-resolver.ts
|
|
58692
|
-
import { existsSync as
|
|
58693
|
-
import { dirname as
|
|
59088
|
+
import { existsSync as existsSync21, readFileSync as readFileSync7 } from "node:fs";
|
|
59089
|
+
import { dirname as dirname9, join as join25, resolve as resolve13 } from "node:path";
|
|
58694
59090
|
function resolveProjectRoot(cwd) {
|
|
58695
|
-
let current =
|
|
59091
|
+
let current = resolve13(cwd);
|
|
58696
59092
|
while (true) {
|
|
58697
|
-
if (
|
|
59093
|
+
if (existsSync21(join25(current, ".fusion"))) {
|
|
58698
59094
|
return current;
|
|
58699
59095
|
}
|
|
58700
|
-
const parent =
|
|
59096
|
+
const parent = dirname9(current);
|
|
58701
59097
|
if (parent === current) {
|
|
58702
|
-
return
|
|
59098
|
+
return resolve13(cwd);
|
|
58703
59099
|
}
|
|
58704
59100
|
current = parent;
|
|
58705
59101
|
}
|
|
58706
59102
|
}
|
|
58707
59103
|
function readJsonObject(path2) {
|
|
58708
|
-
if (!
|
|
59104
|
+
if (!existsSync21(path2)) {
|
|
58709
59105
|
return {};
|
|
58710
59106
|
}
|
|
58711
59107
|
try {
|
|
@@ -58716,8 +59112,8 @@ function readJsonObject(path2) {
|
|
|
58716
59112
|
}
|
|
58717
59113
|
}
|
|
58718
59114
|
function readProjectSettings(projectRootDir) {
|
|
58719
|
-
const fusionSettings =
|
|
58720
|
-
if (
|
|
59115
|
+
const fusionSettings = join25(projectRootDir, ".fusion", "settings.json");
|
|
59116
|
+
if (existsSync21(fusionSettings)) {
|
|
58721
59117
|
const parsed = readJsonObject(fusionSettings);
|
|
58722
59118
|
return {
|
|
58723
59119
|
skills: Array.isArray(parsed.skills) ? parsed.skills : void 0,
|
|
@@ -58963,24 +59359,24 @@ var init_context_limit_detector = __esm({
|
|
|
58963
59359
|
});
|
|
58964
59360
|
|
|
58965
59361
|
// ../engine/src/auth-storage.ts
|
|
58966
|
-
import { existsSync as
|
|
59362
|
+
import { existsSync as existsSync22, readFileSync as readFileSync8 } from "node:fs";
|
|
58967
59363
|
import { homedir as homedir5 } from "node:os";
|
|
58968
|
-
import { join as
|
|
59364
|
+
import { join as join26 } from "node:path";
|
|
58969
59365
|
import { AuthStorage } from "@mariozechner/pi-coding-agent";
|
|
58970
59366
|
import { getOAuthProvider } from "@mariozechner/pi-ai/oauth";
|
|
58971
59367
|
function getHomeDir5() {
|
|
58972
59368
|
return process.env.HOME || process.env.USERPROFILE || homedir5();
|
|
58973
59369
|
}
|
|
58974
59370
|
function getFusionAuthPath(home = getHomeDir5()) {
|
|
58975
|
-
return
|
|
59371
|
+
return join26(home, ".fusion", "agent", "auth.json");
|
|
58976
59372
|
}
|
|
58977
59373
|
function getFusionModelsPath(home = getHomeDir5()) {
|
|
58978
|
-
return
|
|
59374
|
+
return join26(home, ".fusion", "agent", "models.json");
|
|
58979
59375
|
}
|
|
58980
59376
|
function getLegacyAuthPaths(home = getHomeDir5()) {
|
|
58981
59377
|
return [
|
|
58982
|
-
|
|
58983
|
-
|
|
59378
|
+
join26(home, ".pi", "agent", "auth.json"),
|
|
59379
|
+
join26(home, ".pi", "auth.json")
|
|
58984
59380
|
];
|
|
58985
59381
|
}
|
|
58986
59382
|
function getSupplementalAuthPaths(home = getHomeDir5()) {
|
|
@@ -58992,16 +59388,16 @@ function getSupplementalAuthPaths(home = getHomeDir5()) {
|
|
|
58992
59388
|
}
|
|
58993
59389
|
function getLegacyModelsPaths(home = getHomeDir5()) {
|
|
58994
59390
|
return [
|
|
58995
|
-
|
|
58996
|
-
|
|
59391
|
+
join26(home, ".pi", "agent", "models.json"),
|
|
59392
|
+
join26(home, ".pi", "models.json")
|
|
58997
59393
|
];
|
|
58998
59394
|
}
|
|
58999
59395
|
function getModelRegistryModelsPath(home = getHomeDir5()) {
|
|
59000
59396
|
const fusionModelsPath = getFusionModelsPath(home);
|
|
59001
|
-
if (
|
|
59397
|
+
if (existsSync22(fusionModelsPath)) {
|
|
59002
59398
|
return fusionModelsPath;
|
|
59003
59399
|
}
|
|
59004
|
-
return getLegacyModelsPaths(home).find((modelsPath) =>
|
|
59400
|
+
return getLegacyModelsPaths(home).find((modelsPath) => existsSync22(modelsPath)) ?? fusionModelsPath;
|
|
59005
59401
|
}
|
|
59006
59402
|
function readSupplementalCredentials(authPaths = getSupplementalAuthPaths()) {
|
|
59007
59403
|
const credentials = {};
|
|
@@ -59035,7 +59431,7 @@ function resolveStoredCredentialApiKey(providerId, credential) {
|
|
|
59035
59431
|
function readModelsJsonApiKeys(home = getHomeDir5()) {
|
|
59036
59432
|
const apiKeys = /* @__PURE__ */ new Map();
|
|
59037
59433
|
const modelsPath = getModelRegistryModelsPath(home);
|
|
59038
|
-
if (!
|
|
59434
|
+
if (!existsSync22(modelsPath)) {
|
|
59039
59435
|
return apiKeys;
|
|
59040
59436
|
}
|
|
59041
59437
|
try {
|
|
@@ -59199,10 +59595,10 @@ var init_auth_storage = __esm({
|
|
|
59199
59595
|
// ../engine/src/custom-providers.ts
|
|
59200
59596
|
import { readFileSync as readFileSync9 } from "node:fs";
|
|
59201
59597
|
import { homedir as homedir6 } from "node:os";
|
|
59202
|
-
import { join as
|
|
59598
|
+
import { join as join27 } from "node:path";
|
|
59203
59599
|
function readCustomProviders(homeDir = homedir6()) {
|
|
59204
59600
|
try {
|
|
59205
|
-
const settingsPath =
|
|
59601
|
+
const settingsPath = join27(homeDir, ".fusion", "settings.json");
|
|
59206
59602
|
const raw = readFileSync9(settingsPath, "utf-8");
|
|
59207
59603
|
const parsed = JSON.parse(raw);
|
|
59208
59604
|
return Array.isArray(parsed.customProviders) ? parsed.customProviders : [];
|
|
@@ -59286,11 +59682,11 @@ var init_permanent_agent_gating = __esm({
|
|
|
59286
59682
|
});
|
|
59287
59683
|
|
|
59288
59684
|
// ../engine/src/pi.ts
|
|
59289
|
-
import { existsSync as
|
|
59685
|
+
import { existsSync as existsSync23, readFileSync as readFileSync10 } from "node:fs";
|
|
59290
59686
|
import { exec as exec2 } from "node:child_process";
|
|
59291
59687
|
import { promisify as promisify3 } from "node:util";
|
|
59292
59688
|
import { createRequire as createRequire2 } from "node:module";
|
|
59293
|
-
import { basename as basename7, dirname as
|
|
59689
|
+
import { basename as basename7, dirname as dirname10, join as join28, relative as relative4, isAbsolute as isAbsolute8, resolve as resolve14 } from "node:path";
|
|
59294
59690
|
import {
|
|
59295
59691
|
createAgentSession,
|
|
59296
59692
|
createBashTool,
|
|
@@ -59751,7 +60147,7 @@ function isRetryableModelSelectionError(message) {
|
|
|
59751
60147
|
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");
|
|
59752
60148
|
}
|
|
59753
60149
|
function readJsonObject2(path2) {
|
|
59754
|
-
if (!
|
|
60150
|
+
if (!existsSync23(path2)) {
|
|
59755
60151
|
return {};
|
|
59756
60152
|
}
|
|
59757
60153
|
try {
|
|
@@ -59892,17 +60288,17 @@ function siblingAgentDir(agentDir, siblingRoot) {
|
|
|
59892
60288
|
if (basename7(agentDir) !== "agent") {
|
|
59893
60289
|
return void 0;
|
|
59894
60290
|
}
|
|
59895
|
-
return
|
|
60291
|
+
return join28(dirname10(dirname10(agentDir)), siblingRoot, "agent");
|
|
59896
60292
|
}
|
|
59897
60293
|
function createReadOnlyPiSettingsView(cwd, agentDir) {
|
|
59898
60294
|
const projectRoot = resolvePiExtensionProjectRoot(cwd);
|
|
59899
|
-
const fusionAgentDir = agentDir.includes(`${
|
|
59900
|
-
const legacyAgentDir = agentDir.includes(`${
|
|
59901
|
-
const legacyGlobalSettings = legacyAgentDir ? readJsonObject2(
|
|
59902
|
-
const fusionGlobalSettings = fusionAgentDir ? readJsonObject2(
|
|
59903
|
-
const directGlobalSettings = readJsonObject2(
|
|
60295
|
+
const fusionAgentDir = agentDir.includes(`${join28(".fusion", "agent")}`) ? agentDir : siblingAgentDir(agentDir, ".fusion");
|
|
60296
|
+
const legacyAgentDir = agentDir.includes(`${join28(".pi", "agent")}`) ? agentDir : siblingAgentDir(agentDir, ".pi");
|
|
60297
|
+
const legacyGlobalSettings = legacyAgentDir ? readJsonObject2(join28(legacyAgentDir, "settings.json")) : {};
|
|
60298
|
+
const fusionGlobalSettings = fusionAgentDir ? readJsonObject2(join28(fusionAgentDir, "settings.json")) : {};
|
|
60299
|
+
const directGlobalSettings = readJsonObject2(join28(agentDir, "settings.json"));
|
|
59904
60300
|
const globalSettings = { ...legacyGlobalSettings, ...directGlobalSettings, ...fusionGlobalSettings };
|
|
59905
|
-
const fusionProjectSettings = readJsonObject2(
|
|
60301
|
+
const fusionProjectSettings = readJsonObject2(join28(projectRoot, ".fusion", "settings.json"));
|
|
59906
60302
|
const mergedSettings = { ...globalSettings, ...fusionProjectSettings };
|
|
59907
60303
|
return {
|
|
59908
60304
|
getGlobalSettings: () => structuredClone(globalSettings),
|
|
@@ -59913,15 +60309,15 @@ function createReadOnlyPiSettingsView(cwd, agentDir) {
|
|
|
59913
60309
|
function getPackageManagerAgentDir() {
|
|
59914
60310
|
const fusionAgentDir = getFusionAgentDir();
|
|
59915
60311
|
const legacyAgentDir = getLegacyPiAgentDir();
|
|
59916
|
-
const fusionSettings = readJsonObject2(
|
|
59917
|
-
const legacySettings = readJsonObject2(
|
|
59918
|
-
if (hasPackageManagerSettings(fusionSettings) || !
|
|
60312
|
+
const fusionSettings = readJsonObject2(join28(fusionAgentDir, "settings.json"));
|
|
60313
|
+
const legacySettings = readJsonObject2(join28(legacyAgentDir, "settings.json"));
|
|
60314
|
+
if (hasPackageManagerSettings(fusionSettings) || !existsSync23(legacyAgentDir)) {
|
|
59919
60315
|
return fusionAgentDir;
|
|
59920
60316
|
}
|
|
59921
60317
|
if (hasPackageManagerSettings(legacySettings)) {
|
|
59922
60318
|
return legacyAgentDir;
|
|
59923
60319
|
}
|
|
59924
|
-
return
|
|
60320
|
+
return existsSync23(fusionAgentDir) ? fusionAgentDir : legacyAgentDir;
|
|
59925
60321
|
}
|
|
59926
60322
|
function resolveVendoredClaudeCliEntry() {
|
|
59927
60323
|
try {
|
|
@@ -59932,8 +60328,8 @@ function resolveVendoredClaudeCliEntry() {
|
|
|
59932
60328
|
if (!Array.isArray(extensions) || extensions.length === 0) return null;
|
|
59933
60329
|
const entry = extensions[0];
|
|
59934
60330
|
if (typeof entry !== "string" || entry.length === 0) return null;
|
|
59935
|
-
const path2 =
|
|
59936
|
-
return
|
|
60331
|
+
const path2 = resolve14(dirname10(pkgJsonPath), entry);
|
|
60332
|
+
return existsSync23(path2) ? path2 : null;
|
|
59937
60333
|
} catch {
|
|
59938
60334
|
return null;
|
|
59939
60335
|
}
|
|
@@ -59947,8 +60343,8 @@ function resolveVendoredDroidCliEntry() {
|
|
|
59947
60343
|
if (!Array.isArray(extensions) || extensions.length === 0) return null;
|
|
59948
60344
|
const entry = extensions[0];
|
|
59949
60345
|
if (typeof entry !== "string" || entry.length === 0) return null;
|
|
59950
|
-
const path2 =
|
|
59951
|
-
return
|
|
60346
|
+
const path2 = resolve14(dirname10(pkgJsonPath), entry);
|
|
60347
|
+
return existsSync23(path2) ? path2 : null;
|
|
59952
60348
|
} catch {
|
|
59953
60349
|
return null;
|
|
59954
60350
|
}
|
|
@@ -59976,7 +60372,7 @@ async function registerExtensionProviders(cwd, modelRegistry) {
|
|
|
59976
60372
|
const extensionsResult = await discoverAndLoadExtensions(
|
|
59977
60373
|
doubleReconciledPaths,
|
|
59978
60374
|
cwd,
|
|
59979
|
-
|
|
60375
|
+
join28(resolvePiExtensionProjectRoot(cwd), ".fusion", "disabled-auto-extension-discovery")
|
|
59980
60376
|
);
|
|
59981
60377
|
for (const { path: path2, error } of extensionsResult.errors) {
|
|
59982
60378
|
extensionsLog.warn(`Failed to load ${path2}: ${error}`);
|
|
@@ -60011,9 +60407,9 @@ async function isRegisteredGitWorktree(projectRoot, worktreePath) {
|
|
|
60011
60407
|
cwd: projectRoot,
|
|
60012
60408
|
encoding: "utf-8"
|
|
60013
60409
|
});
|
|
60014
|
-
const resolvedWorktree =
|
|
60410
|
+
const resolvedWorktree = resolve14(worktreePath);
|
|
60015
60411
|
return stdout.split("\n").some(
|
|
60016
|
-
(line) => line.startsWith("worktree ") &&
|
|
60412
|
+
(line) => line.startsWith("worktree ") && resolve14(line.slice("worktree ".length)) === resolvedWorktree
|
|
60017
60413
|
);
|
|
60018
60414
|
} catch {
|
|
60019
60415
|
return false;
|
|
@@ -60025,16 +60421,16 @@ async function isCompleteGitWorktree(worktreePath) {
|
|
|
60025
60421
|
cwd: worktreePath,
|
|
60026
60422
|
encoding: "utf-8"
|
|
60027
60423
|
});
|
|
60028
|
-
return
|
|
60424
|
+
return resolve14(stdout.trim()) === resolve14(worktreePath);
|
|
60029
60425
|
} catch {
|
|
60030
60426
|
return false;
|
|
60031
60427
|
}
|
|
60032
60428
|
}
|
|
60033
60429
|
async function assertValidWorktreeSession(cwd, projectRoot) {
|
|
60034
|
-
if (!
|
|
60430
|
+
if (!existsSync23(cwd)) {
|
|
60035
60431
|
throw new Error(`Refusing to start coding agent in missing worktree: ${cwd}`);
|
|
60036
60432
|
}
|
|
60037
|
-
if (!
|
|
60433
|
+
if (!existsSync23(join28(cwd, ".git")) || !await isCompleteGitWorktree(cwd)) {
|
|
60038
60434
|
throw new Error(`Refusing to start coding agent in incomplete worktree: ${cwd}`);
|
|
60039
60435
|
}
|
|
60040
60436
|
if (!await isRegisteredGitWorktree(projectRoot, cwd)) {
|
|
@@ -60042,14 +60438,14 @@ async function assertValidWorktreeSession(cwd, projectRoot) {
|
|
|
60042
60438
|
}
|
|
60043
60439
|
}
|
|
60044
60440
|
function isWorktreeAllowedPath(worktreePath, projectRoot, requestedPath, toolName) {
|
|
60045
|
-
const worktreeResolved =
|
|
60046
|
-
const projectRootResolved =
|
|
60047
|
-
const requestedResolved =
|
|
60048
|
-
const relToWorktree =
|
|
60049
|
-
if (!relToWorktree.startsWith("..") && !
|
|
60441
|
+
const worktreeResolved = resolve14(worktreePath);
|
|
60442
|
+
const projectRootResolved = resolve14(projectRoot);
|
|
60443
|
+
const requestedResolved = isAbsolute8(requestedPath) ? resolve14(requestedPath) : resolve14(worktreeResolved, requestedPath);
|
|
60444
|
+
const relToWorktree = relative4(worktreeResolved, requestedResolved);
|
|
60445
|
+
if (!relToWorktree.startsWith("..") && !isAbsolute8(relToWorktree)) {
|
|
60050
60446
|
return true;
|
|
60051
60447
|
}
|
|
60052
|
-
const relToProjectRoot =
|
|
60448
|
+
const relToProjectRoot = relative4(projectRootResolved, requestedResolved).replace(/\\/g, "/");
|
|
60053
60449
|
if (relToProjectRoot === ".fusion/memory" || relToProjectRoot === ".fusion/memory/" || relToProjectRoot.startsWith(".fusion/memory/")) {
|
|
60054
60450
|
return true;
|
|
60055
60451
|
}
|
|
@@ -60103,7 +60499,7 @@ function wrapToolsWithBoundary(tools, worktreePath, projectRoot) {
|
|
|
60103
60499
|
const _signal = args[2];
|
|
60104
60500
|
const pathArg = params.path;
|
|
60105
60501
|
if (pathArg && !isWorktreeAllowedPath(worktreePath, projectRoot, pathArg, tool.name)) {
|
|
60106
|
-
const relToProject =
|
|
60502
|
+
const relToProject = relative4(projectRoot, pathArg);
|
|
60107
60503
|
return boundaryRejection(
|
|
60108
60504
|
`Path "${relToProject}" is outside the worktree boundary. Coding agents can only modify files inside the current worktree. Exceptions (read-only): .fusion/memory/, .fusion/tasks/*/attachments/, and .fusion/tasks/*/{PROMPT.md,task.json} for dependency context.`
|
|
60109
60505
|
);
|
|
@@ -60852,7 +61248,7 @@ ${source.content ?? ""}`;
|
|
|
60852
61248
|
|
|
60853
61249
|
// ../engine/src/research/providers/local-docs-provider.ts
|
|
60854
61250
|
import { promises as fs } from "node:fs";
|
|
60855
|
-
import { extname as extname2, join as
|
|
61251
|
+
import { extname as extname2, join as join29, relative as relative5, resolve as resolve15 } from "node:path";
|
|
60856
61252
|
function buildExcerpt(content, terms) {
|
|
60857
61253
|
const lower = content.toLowerCase();
|
|
60858
61254
|
const first = terms.find((term) => lower.includes(term));
|
|
@@ -60889,7 +61285,7 @@ var init_local_docs_provider = __esm({
|
|
|
60889
61285
|
LocalDocsProvider = class {
|
|
60890
61286
|
constructor(options) {
|
|
60891
61287
|
this.options = options;
|
|
60892
|
-
this.projectRoot =
|
|
61288
|
+
this.projectRoot = resolve15(options.projectRoot);
|
|
60893
61289
|
this.scanPaths = options.scanPaths ?? ["docs", "README.md", "AGENTS.md", ".fusion/memory"];
|
|
60894
61290
|
}
|
|
60895
61291
|
type = "local-docs";
|
|
@@ -60915,7 +61311,7 @@ var init_local_docs_provider = __esm({
|
|
|
60915
61311
|
score += matches?.length ?? 0;
|
|
60916
61312
|
}
|
|
60917
61313
|
if (score <= 0) continue;
|
|
60918
|
-
const relPath =
|
|
61314
|
+
const relPath = relative5(this.projectRoot, file);
|
|
60919
61315
|
results.push({
|
|
60920
61316
|
score,
|
|
60921
61317
|
source: {
|
|
@@ -60933,7 +61329,7 @@ var init_local_docs_provider = __esm({
|
|
|
60933
61329
|
}
|
|
60934
61330
|
async fetchContent(filePath, config = {}, signal) {
|
|
60935
61331
|
const timeoutMs = Number(config.timeoutMs ?? this.options.timeoutMs ?? DEFAULT_TIMEOUT_MS3);
|
|
60936
|
-
const resolvedPath =
|
|
61332
|
+
const resolvedPath = resolve15(this.projectRoot, filePath);
|
|
60937
61333
|
if (!resolvedPath.startsWith(this.projectRoot)) {
|
|
60938
61334
|
throw new ResearchProviderError({
|
|
60939
61335
|
providerType: "local-docs",
|
|
@@ -60941,8 +61337,8 @@ var init_local_docs_provider = __esm({
|
|
|
60941
61337
|
message: "Path traversal is not allowed"
|
|
60942
61338
|
});
|
|
60943
61339
|
}
|
|
60944
|
-
const
|
|
60945
|
-
if (!
|
|
61340
|
+
const stat9 = await this.withTimeout(fs.stat(resolvedPath), timeoutMs, signal);
|
|
61341
|
+
if (!stat9.isFile()) {
|
|
60946
61342
|
throw new ResearchProviderError({ providerType: "local-docs", code: "provider-unavailable", message: "Path is not a file" });
|
|
60947
61343
|
}
|
|
60948
61344
|
const content = await this.withTimeout(fs.readFile(resolvedPath), timeoutMs, signal);
|
|
@@ -60954,9 +61350,9 @@ var init_local_docs_provider = __esm({
|
|
|
60954
61350
|
return {
|
|
60955
61351
|
content: text.length > MAX_FILE_SIZE_BYTES ? text.slice(0, MAX_FILE_SIZE_BYTES) : text,
|
|
60956
61352
|
metadata: {
|
|
60957
|
-
path:
|
|
60958
|
-
size:
|
|
60959
|
-
modifiedAt:
|
|
61353
|
+
path: relative5(this.projectRoot, resolvedPath),
|
|
61354
|
+
size: stat9.size,
|
|
61355
|
+
modifiedAt: stat9.mtime.toISOString(),
|
|
60960
61356
|
extension: extname2(resolvedPath)
|
|
60961
61357
|
},
|
|
60962
61358
|
mimeType: "text/plain"
|
|
@@ -60966,13 +61362,13 @@ var init_local_docs_provider = __esm({
|
|
|
60966
61362
|
const ignorePatterns = await this.readGitignore();
|
|
60967
61363
|
const files = [];
|
|
60968
61364
|
for (const pathEntry of this.scanPaths) {
|
|
60969
|
-
const target =
|
|
61365
|
+
const target = resolve15(this.projectRoot, pathEntry);
|
|
60970
61366
|
if (!target.startsWith(this.projectRoot)) continue;
|
|
60971
61367
|
try {
|
|
60972
|
-
const
|
|
60973
|
-
if (
|
|
61368
|
+
const stat9 = await fs.stat(target);
|
|
61369
|
+
if (stat9.isDirectory()) {
|
|
60974
61370
|
await this.walk(target, files, ignorePatterns, signal);
|
|
60975
|
-
} else if (
|
|
61371
|
+
} else if (stat9.isFile()) {
|
|
60976
61372
|
files.push(target);
|
|
60977
61373
|
}
|
|
60978
61374
|
} catch {
|
|
@@ -60981,7 +61377,7 @@ var init_local_docs_provider = __esm({
|
|
|
60981
61377
|
const rootEntries = await fs.readdir(this.projectRoot, { withFileTypes: true });
|
|
60982
61378
|
for (const entry of rootEntries) {
|
|
60983
61379
|
if (entry.isFile() && entry.name.toLowerCase().endsWith(".md")) {
|
|
60984
|
-
files.push(
|
|
61380
|
+
files.push(join29(this.projectRoot, entry.name));
|
|
60985
61381
|
}
|
|
60986
61382
|
}
|
|
60987
61383
|
return [...new Set(files)];
|
|
@@ -60991,8 +61387,8 @@ var init_local_docs_provider = __esm({
|
|
|
60991
61387
|
const entries = await fs.readdir(dir, { withFileTypes: true });
|
|
60992
61388
|
for (const entry of entries) {
|
|
60993
61389
|
this.throwIfAborted(signal);
|
|
60994
|
-
const fullPath =
|
|
60995
|
-
const relPath =
|
|
61390
|
+
const fullPath = join29(dir, entry.name);
|
|
61391
|
+
const relPath = relative5(this.projectRoot, fullPath).replace(/\\/g, "/");
|
|
60996
61392
|
if (matchesGitignore(relPath, ignorePatterns)) continue;
|
|
60997
61393
|
if (entry.isDirectory()) {
|
|
60998
61394
|
await this.walk(fullPath, out, ignorePatterns, signal);
|
|
@@ -61003,8 +61399,8 @@ var init_local_docs_provider = __esm({
|
|
|
61003
61399
|
}
|
|
61004
61400
|
async safeReadText(filePath, signal) {
|
|
61005
61401
|
try {
|
|
61006
|
-
const
|
|
61007
|
-
if (
|
|
61402
|
+
const stat9 = await fs.stat(filePath);
|
|
61403
|
+
if (stat9.size > MAX_FILE_SIZE_BYTES) return void 0;
|
|
61008
61404
|
const content = await fs.readFile(filePath);
|
|
61009
61405
|
if (content.subarray(0, BINARY_SNIFF_BYTES).includes(0)) return void 0;
|
|
61010
61406
|
this.throwIfAborted(signal);
|
|
@@ -61016,7 +61412,7 @@ var init_local_docs_provider = __esm({
|
|
|
61016
61412
|
}
|
|
61017
61413
|
async readGitignore() {
|
|
61018
61414
|
try {
|
|
61019
|
-
const content = await fs.readFile(
|
|
61415
|
+
const content = await fs.readFile(join29(this.projectRoot, ".gitignore"), "utf-8");
|
|
61020
61416
|
return content.split(/\r?\n/).map((line) => line.trim()).filter((line) => line && !line.startsWith("#"));
|
|
61021
61417
|
} catch {
|
|
61022
61418
|
return [];
|
|
@@ -61349,8 +61745,8 @@ var init_page_fetch_provider = __esm({
|
|
|
61349
61745
|
|
|
61350
61746
|
// ../engine/src/research/providers/web-search-provider.ts
|
|
61351
61747
|
async function sleep(ms, signal) {
|
|
61352
|
-
await new Promise((
|
|
61353
|
-
const timer = setTimeout(
|
|
61748
|
+
await new Promise((resolve24, reject) => {
|
|
61749
|
+
const timer = setTimeout(resolve24, ms);
|
|
61354
61750
|
const onAbort = () => {
|
|
61355
61751
|
clearTimeout(timer);
|
|
61356
61752
|
reject(new ResearchProviderError({ providerType: "web-search", code: "abort", message: "Search aborted" }));
|
|
@@ -61818,31 +62214,31 @@ var init_research_step_runner = __esm({
|
|
|
61818
62214
|
});
|
|
61819
62215
|
|
|
61820
62216
|
// ../engine/src/agent-tools.ts
|
|
61821
|
-
import { appendFile as appendFile3, mkdir as
|
|
61822
|
-
import { existsSync as
|
|
62217
|
+
import { appendFile as appendFile3, mkdir as mkdir12, readFile as readFile14, readdir as readdir8, stat as stat5, writeFile as writeFile11 } from "node:fs/promises";
|
|
62218
|
+
import { existsSync as existsSync24 } from "node:fs";
|
|
61823
62219
|
import { createHash as createHash5 } from "node:crypto";
|
|
61824
|
-
import { join as
|
|
62220
|
+
import { join as join30, relative as relative6, resolve as resolve16 } from "node:path";
|
|
61825
62221
|
import { Type } from "@mariozechner/pi-ai";
|
|
61826
62222
|
function sanitizeAgentMemoryId(agentId) {
|
|
61827
62223
|
return agentId.trim().replace(/[^a-zA-Z0-9._-]+/g, "-").replace(/^-+|-+$/g, "") || "agent";
|
|
61828
62224
|
}
|
|
61829
62225
|
function agentMemoryDisplayPath(agentId) {
|
|
61830
|
-
return `${
|
|
62226
|
+
return `${AGENT_MEMORY_ROOT3}/${sanitizeAgentMemoryId(agentId)}/${AGENT_MEMORY_FILENAME2}`;
|
|
61831
62227
|
}
|
|
61832
62228
|
function agentDreamsDisplayPath(agentId) {
|
|
61833
|
-
return `${
|
|
62229
|
+
return `${AGENT_MEMORY_ROOT3}/${sanitizeAgentMemoryId(agentId)}/${AGENT_DREAMS_FILENAME2}`;
|
|
61834
62230
|
}
|
|
61835
62231
|
function agentMemoryDirectory(rootDir, agentId) {
|
|
61836
|
-
return
|
|
62232
|
+
return join30(rootDir, AGENT_MEMORY_ROOT3, sanitizeAgentMemoryId(agentId));
|
|
61837
62233
|
}
|
|
61838
62234
|
function agentMemoryFilePath(rootDir, agentId) {
|
|
61839
|
-
return
|
|
62235
|
+
return join30(agentMemoryDirectory(rootDir, agentId), AGENT_MEMORY_FILENAME2);
|
|
61840
62236
|
}
|
|
61841
62237
|
function agentDreamsFilePath(rootDir, agentId) {
|
|
61842
|
-
return
|
|
62238
|
+
return join30(agentMemoryDirectory(rootDir, agentId), AGENT_DREAMS_FILENAME2);
|
|
61843
62239
|
}
|
|
61844
62240
|
function agentDailyFilePath(rootDir, agentId, date = /* @__PURE__ */ new Date()) {
|
|
61845
|
-
return
|
|
62241
|
+
return join30(agentMemoryDirectory(rootDir, agentId), `${date.toISOString().slice(0, 10)}.md`);
|
|
61846
62242
|
}
|
|
61847
62243
|
async function readAgentMemoryWorkspaceLongTerm(rootDir, agentId) {
|
|
61848
62244
|
const safeRoot = typeof rootDir === "string" ? rootDir.trim() : "";
|
|
@@ -61852,11 +62248,11 @@ async function readAgentMemoryWorkspaceLongTerm(rootDir, agentId) {
|
|
|
61852
62248
|
}
|
|
61853
62249
|
const filePath = agentMemoryFilePath(safeRoot, safeAgentId);
|
|
61854
62250
|
try {
|
|
61855
|
-
const fileStat = await
|
|
62251
|
+
const fileStat = await stat5(filePath);
|
|
61856
62252
|
if (!fileStat.isFile()) {
|
|
61857
62253
|
return "";
|
|
61858
62254
|
}
|
|
61859
|
-
const content = await
|
|
62255
|
+
const content = await readFile14(filePath, "utf-8");
|
|
61860
62256
|
return typeof content === "string" ? content.trim() : "";
|
|
61861
62257
|
} catch {
|
|
61862
62258
|
return "";
|
|
@@ -61894,9 +62290,9 @@ async function syncAgentMemoryFile(rootDir, agentMemory) {
|
|
|
61894
62290
|
return null;
|
|
61895
62291
|
}
|
|
61896
62292
|
const dir = agentMemoryDirectory(rootDir, agentMemory.agentId);
|
|
61897
|
-
await
|
|
62293
|
+
await mkdir12(dir, { recursive: true });
|
|
61898
62294
|
const longTermPath = agentMemoryFilePath(rootDir, agentMemory.agentId);
|
|
61899
|
-
if (!
|
|
62295
|
+
if (!existsSync24(longTermPath)) {
|
|
61900
62296
|
const title = agentMemory.agentName?.trim() ? `# Agent Memory: ${agentMemory.agentName.trim()}` : "# Agent Memory";
|
|
61901
62297
|
const fileContent = `${title}
|
|
61902
62298
|
|
|
@@ -61904,15 +62300,15 @@ async function syncAgentMemoryFile(rootDir, agentMemory) {
|
|
|
61904
62300
|
|
|
61905
62301
|
${content || ""}
|
|
61906
62302
|
`;
|
|
61907
|
-
await
|
|
62303
|
+
await writeFile11(longTermPath, fileContent, "utf-8");
|
|
61908
62304
|
}
|
|
61909
62305
|
const dreamsPath = agentDreamsFilePath(rootDir, agentMemory.agentId);
|
|
61910
|
-
if (!
|
|
61911
|
-
await
|
|
62306
|
+
if (!existsSync24(dreamsPath)) {
|
|
62307
|
+
await writeFile11(dreamsPath, "# Agent Memory Dreams\n\n<!-- Synthesized patterns from this agent's daily notes. -->\n", "utf-8");
|
|
61912
62308
|
}
|
|
61913
62309
|
const dailyPath = agentDailyFilePath(rootDir, agentMemory.agentId);
|
|
61914
|
-
if (!
|
|
61915
|
-
await
|
|
62310
|
+
if (!existsSync24(dailyPath)) {
|
|
62311
|
+
await writeFile11(dailyPath, `# Agent Daily Memory ${(/* @__PURE__ */ new Date()).toISOString().slice(0, 10)}
|
|
61916
62312
|
|
|
61917
62313
|
<!-- Running observations for this agent. -->
|
|
61918
62314
|
`, "utf-8");
|
|
@@ -61928,19 +62324,19 @@ async function listAgentMemoryFiles2(rootDir, agentMemory) {
|
|
|
61928
62324
|
];
|
|
61929
62325
|
let entries;
|
|
61930
62326
|
try {
|
|
61931
|
-
entries = await
|
|
62327
|
+
entries = await readdir8(dir);
|
|
61932
62328
|
} catch (err) {
|
|
61933
62329
|
log10.warn(`Failed to read agent memory directory ${dir}: ${err instanceof Error ? err.message : String(err)}`);
|
|
61934
62330
|
entries = [];
|
|
61935
62331
|
}
|
|
61936
62332
|
for (const entry of entries) {
|
|
61937
62333
|
if (!DAILY_AGENT_MEMORY_RE2.test(entry)) continue;
|
|
61938
|
-
const absPath =
|
|
61939
|
-
const fileStat = await
|
|
62334
|
+
const absPath = join30(dir, entry);
|
|
62335
|
+
const fileStat = await stat5(absPath);
|
|
61940
62336
|
if (fileStat.isFile()) {
|
|
61941
62337
|
files.push({
|
|
61942
62338
|
absPath,
|
|
61943
|
-
displayPath: `${
|
|
62339
|
+
displayPath: `${AGENT_MEMORY_ROOT3}/${sanitizeAgentMemoryId(agentMemory.agentId)}/${entry}`
|
|
61944
62340
|
});
|
|
61945
62341
|
}
|
|
61946
62342
|
}
|
|
@@ -61958,7 +62354,7 @@ async function searchAgentMemoryFile(rootDir, agentMemory, query, limit) {
|
|
|
61958
62354
|
}
|
|
61959
62355
|
const results = [];
|
|
61960
62356
|
for (const file of await listAgentMemoryFiles2(rootDir, agentMemory)) {
|
|
61961
|
-
const content = await
|
|
62357
|
+
const content = await readFile14(file.absPath, "utf-8");
|
|
61962
62358
|
const lines = content.split("\n");
|
|
61963
62359
|
for (let index = 0; index < lines.length; index += 8) {
|
|
61964
62360
|
const chunk = lines.slice(index, index + 12).join("\n").trim();
|
|
@@ -62036,13 +62432,13 @@ function normalizeQmdAgentMemoryResultPath(rootDir, agentId, rawPath) {
|
|
|
62036
62432
|
candidate = candidate.split("?")[0]?.split("#")[0] ?? "";
|
|
62037
62433
|
candidate = candidate.replace(/^\.\/+/, "");
|
|
62038
62434
|
const normalizedAgentId = sanitizeAgentMemoryId(agentId);
|
|
62039
|
-
const agentPrefix = `${
|
|
62435
|
+
const agentPrefix = `${AGENT_MEMORY_ROOT3}/${normalizedAgentId}/`;
|
|
62040
62436
|
if (candidate.startsWith(agentPrefix)) {
|
|
62041
62437
|
return resolveAgentMemoryPath(rootDir, agentId, candidate)?.displayPath ?? fallbackPath;
|
|
62042
62438
|
}
|
|
62043
|
-
const workspacePath =
|
|
62044
|
-
const candidateAbs =
|
|
62045
|
-
const relToWorkspace =
|
|
62439
|
+
const workspacePath = resolve16(agentMemoryDirectory(rootDir, agentId)).replace(/\\/g, "/");
|
|
62440
|
+
const candidateAbs = resolve16(rootDir, candidate).replace(/\\/g, "/");
|
|
62441
|
+
const relToWorkspace = relative6(workspacePath, candidateAbs).replace(/\\/g, "/");
|
|
62046
62442
|
if (relToWorkspace && !relToWorkspace.startsWith("..") && !relToWorkspace.includes("/../")) {
|
|
62047
62443
|
const maybeDisplayPath = `${agentPrefix}${relToWorkspace}`;
|
|
62048
62444
|
return resolveAgentMemoryPath(rootDir, agentId, maybeDisplayPath)?.displayPath ?? fallbackPath;
|
|
@@ -62095,7 +62491,7 @@ async function searchAgentMemoryWithQmd(rootDir, agentMemory, query, limit) {
|
|
|
62095
62491
|
}
|
|
62096
62492
|
function resolveAgentMemoryPath(rootDir, agentId, path2) {
|
|
62097
62493
|
const safeAgentId = sanitizeAgentMemoryId(agentId);
|
|
62098
|
-
const prefix = `${
|
|
62494
|
+
const prefix = `${AGENT_MEMORY_ROOT3}/${safeAgentId}/`;
|
|
62099
62495
|
if (!path2.startsWith(prefix)) {
|
|
62100
62496
|
return null;
|
|
62101
62497
|
}
|
|
@@ -62104,7 +62500,7 @@ function resolveAgentMemoryPath(rootDir, agentId, path2) {
|
|
|
62104
62500
|
return null;
|
|
62105
62501
|
}
|
|
62106
62502
|
return {
|
|
62107
|
-
absPath:
|
|
62503
|
+
absPath: join30(agentMemoryDirectory(rootDir, agentId), filename),
|
|
62108
62504
|
displayPath: `${prefix}${filename}`
|
|
62109
62505
|
};
|
|
62110
62506
|
}
|
|
@@ -62114,7 +62510,7 @@ async function getAgentMemoryWindow(rootDir, agentMemory, path2, startLine = 1,
|
|
|
62114
62510
|
return null;
|
|
62115
62511
|
}
|
|
62116
62512
|
await syncAgentMemoryFile(rootDir, agentMemory);
|
|
62117
|
-
const content = await
|
|
62513
|
+
const content = await readFile14(resolved.absPath, "utf-8");
|
|
62118
62514
|
const lines = content.split("\n");
|
|
62119
62515
|
const start = Math.max(1, Math.floor(startLine));
|
|
62120
62516
|
const count = Math.max(1, Math.min(Math.floor(lineCount), 200));
|
|
@@ -63123,9 +63519,9 @@ function createResearchTools(options) {
|
|
|
63123
63519
|
const maxWaitMs = Math.max(1e3, Math.min(params.max_wait_ms ?? 9e4, resolved.limits.maxDurationMs));
|
|
63124
63520
|
const completed = await Promise.race([
|
|
63125
63521
|
runPromise,
|
|
63126
|
-
new Promise((
|
|
63522
|
+
new Promise((resolve24) => setTimeout(() => {
|
|
63127
63523
|
const latest = options.store.getResearchStore().getRun(runId);
|
|
63128
|
-
|
|
63524
|
+
resolve24(latest ?? {
|
|
63129
63525
|
id: runId,
|
|
63130
63526
|
query: params.query,
|
|
63131
63527
|
status: "running",
|
|
@@ -63251,7 +63647,7 @@ ${lines.join("\n")}`
|
|
|
63251
63647
|
}
|
|
63252
63648
|
};
|
|
63253
63649
|
}
|
|
63254
|
-
var taskCreateParams, taskLogParams, taskDocumentWriteParams, taskDocumentReadParams, reflectOnPerformanceParams, readEvaluationsParams, updateIdentityParams, listAgentsParams, delegateTaskParams, getAgentConfigParams, updateAgentConfigParams, createAgentParams, deleteAgentParams, sendMessageParams, readMessagesParams, memorySearchParams, memoryGetParams, webFetchParams, researchRunParams, researchListParams, researchGetParams, researchCancelParams, memoryAppendParams, log10, MAX_INSTRUCTIONS_TEXT_LENGTH, MAX_MEMORY_LENGTH, MAX_SOUL_LENGTH,
|
|
63650
|
+
var taskCreateParams, taskLogParams, taskDocumentWriteParams, taskDocumentReadParams, reflectOnPerformanceParams, readEvaluationsParams, updateIdentityParams, listAgentsParams, delegateTaskParams, getAgentConfigParams, updateAgentConfigParams, createAgentParams, deleteAgentParams, sendMessageParams, readMessagesParams, memorySearchParams, memoryGetParams, webFetchParams, researchRunParams, researchListParams, researchGetParams, researchCancelParams, memoryAppendParams, log10, MAX_INSTRUCTIONS_TEXT_LENGTH, MAX_MEMORY_LENGTH, MAX_SOUL_LENGTH, AGENT_MEMORY_ROOT3, AGENT_MEMORY_FILENAME2, AGENT_DREAMS_FILENAME2, agentQmdRefreshState, AGENT_QMD_REFRESH_INTERVAL_MS, DAILY_AGENT_MEMORY_RE2;
|
|
63255
63651
|
var init_agent_tools = __esm({
|
|
63256
63652
|
"../engine/src/agent-tools.ts"() {
|
|
63257
63653
|
"use strict";
|
|
@@ -63422,7 +63818,7 @@ var init_agent_tools = __esm({
|
|
|
63422
63818
|
MAX_INSTRUCTIONS_TEXT_LENGTH = 5e4;
|
|
63423
63819
|
MAX_MEMORY_LENGTH = 5e4;
|
|
63424
63820
|
MAX_SOUL_LENGTH = 1e4;
|
|
63425
|
-
|
|
63821
|
+
AGENT_MEMORY_ROOT3 = ".fusion/agent-memory";
|
|
63426
63822
|
AGENT_MEMORY_FILENAME2 = "MEMORY.md";
|
|
63427
63823
|
AGENT_DREAMS_FILENAME2 = "DREAMS.md";
|
|
63428
63824
|
agentQmdRefreshState = /* @__PURE__ */ new Map();
|
|
@@ -63962,9 +64358,9 @@ var init_usage_limit_detector = __esm({
|
|
|
63962
64358
|
});
|
|
63963
64359
|
|
|
63964
64360
|
// ../engine/src/agent-instructions.ts
|
|
63965
|
-
import { readFile as
|
|
64361
|
+
import { readFile as readFile15, writeFile as writeFile12, mkdir as mkdir13, access as access4 } from "node:fs/promises";
|
|
63966
64362
|
import { constants as fsConstants2 } from "node:fs";
|
|
63967
|
-
import { isAbsolute as
|
|
64363
|
+
import { isAbsolute as isAbsolute9, resolve as resolve17, relative as relative7, normalize as normalize4, sep as sep6, dirname as dirname11 } from "node:path";
|
|
63968
64364
|
function trimAndClamp(value, maxLength, label, agentId) {
|
|
63969
64365
|
const trimmed = value.trim();
|
|
63970
64366
|
if (!trimmed) {
|
|
@@ -63994,18 +64390,18 @@ function resolveValidatedMarkdownPath(rawPath, rootDir, agentId, fieldLabel) {
|
|
|
63994
64390
|
log12.warn(`${fieldLabel} must end in .md for agent ${agentId}: ${trimmed}`);
|
|
63995
64391
|
return null;
|
|
63996
64392
|
}
|
|
63997
|
-
if (
|
|
64393
|
+
if (isAbsolute9(trimmed)) {
|
|
63998
64394
|
log12.warn(`${fieldLabel} must be project-relative for agent ${agentId}: ${trimmed}`);
|
|
63999
64395
|
return null;
|
|
64000
64396
|
}
|
|
64001
|
-
const normalized =
|
|
64397
|
+
const normalized = normalize4(trimmed);
|
|
64002
64398
|
if (isPathTraversal2(normalized)) {
|
|
64003
64399
|
log12.warn(`${fieldLabel} traversal is not allowed for agent ${agentId}: ${trimmed}`);
|
|
64004
64400
|
return null;
|
|
64005
64401
|
}
|
|
64006
|
-
const resolvedPath =
|
|
64007
|
-
const rel =
|
|
64008
|
-
if (!rel || rel.startsWith(`..${
|
|
64402
|
+
const resolvedPath = resolve17(rootDir, normalized);
|
|
64403
|
+
const rel = relative7(rootDir, resolvedPath);
|
|
64404
|
+
if (!rel || rel.startsWith(`..${sep6}`) || rel === ".." || isAbsolute9(rel)) {
|
|
64009
64405
|
log12.warn(`${fieldLabel} escapes project root for agent ${agentId}: ${trimmed}`);
|
|
64010
64406
|
return null;
|
|
64011
64407
|
}
|
|
@@ -64024,7 +64420,7 @@ async function resolveAgentHeartbeatProcedure(agent, rootDir) {
|
|
|
64024
64420
|
return null;
|
|
64025
64421
|
}
|
|
64026
64422
|
try {
|
|
64027
|
-
const content = await
|
|
64423
|
+
const content = await readFile15(filePath, "utf-8");
|
|
64028
64424
|
const normalized = trimAndClamp(
|
|
64029
64425
|
content,
|
|
64030
64426
|
MAX_INSTRUCTIONS_TEXT_LENGTH2,
|
|
@@ -64053,8 +64449,8 @@ async function ensureDefaultHeartbeatProcedureFile(rootDir, procedurePathRel, de
|
|
|
64053
64449
|
} catch {
|
|
64054
64450
|
}
|
|
64055
64451
|
try {
|
|
64056
|
-
await
|
|
64057
|
-
await
|
|
64452
|
+
await mkdir13(dirname11(filePath), { recursive: true });
|
|
64453
|
+
await writeFile12(filePath, defaultContent, "utf-8");
|
|
64058
64454
|
log12.log(`Seeded default heartbeat procedure file at ${filePath}`);
|
|
64059
64455
|
return filePath;
|
|
64060
64456
|
} catch (err) {
|
|
@@ -64154,7 +64550,7 @@ async function resolveAgentInstructions(agent, rootDir, ratingSummary) {
|
|
|
64154
64550
|
const filePath = resolveValidatedInstructionsPath(agent.instructionsPath, rootDir, agent.id);
|
|
64155
64551
|
if (filePath) {
|
|
64156
64552
|
try {
|
|
64157
|
-
const content = await
|
|
64553
|
+
const content = await readFile15(filePath, "utf-8");
|
|
64158
64554
|
const normalizedContent = trimAndClamp(
|
|
64159
64555
|
content,
|
|
64160
64556
|
MAX_INSTRUCTIONS_TEXT_LENGTH2,
|
|
@@ -64282,6 +64678,16 @@ var init_agent_instructions = __esm({
|
|
|
64282
64678
|
});
|
|
64283
64679
|
|
|
64284
64680
|
// ../engine/src/notification/ntfy-provider.ts
|
|
64681
|
+
function resolveParticipantLabel(metadata, kind) {
|
|
64682
|
+
const nameKey = kind === "from" ? "fromName" : "toName";
|
|
64683
|
+
const idKey = kind === "from" ? "fromId" : "toId";
|
|
64684
|
+
const name = typeof metadata?.[nameKey] === "string" ? metadata[nameKey].trim() : "";
|
|
64685
|
+
if (name.length > 0) {
|
|
64686
|
+
return name;
|
|
64687
|
+
}
|
|
64688
|
+
const id = typeof metadata?.[idKey] === "string" ? metadata[idKey].trim() : "";
|
|
64689
|
+
return id.length > 0 ? id : kind === "from" ? "agent" : "recipient";
|
|
64690
|
+
}
|
|
64285
64691
|
var SUPPORTED_EVENTS, NtfyNotificationProvider;
|
|
64286
64692
|
var init_ntfy_provider = __esm({
|
|
64287
64693
|
"../engine/src/notification/ntfy-provider.ts"() {
|
|
@@ -64348,8 +64754,8 @@ var init_ntfy_provider = __esm({
|
|
|
64348
64754
|
};
|
|
64349
64755
|
const identifier = formatTaskIdentifier(taskLike);
|
|
64350
64756
|
const messageId = typeof payload.metadata?.messageId === "string" ? payload.metadata.messageId : void 0;
|
|
64351
|
-
const senderLabel =
|
|
64352
|
-
const recipientLabel =
|
|
64757
|
+
const senderLabel = resolveParticipantLabel(payload.metadata, "from");
|
|
64758
|
+
const recipientLabel = resolveParticipantLabel(payload.metadata, "to");
|
|
64353
64759
|
const preview = typeof payload.metadata?.preview === "string" ? payload.metadata.preview : "(no preview)";
|
|
64354
64760
|
const replyToMessageId = typeof payload.metadata?.replyToMessageId === "string" ? payload.metadata.replyToMessageId : void 0;
|
|
64355
64761
|
const clickUrl = buildNtfyClickUrl({
|
|
@@ -64441,6 +64847,16 @@ var init_ntfy_provider = __esm({
|
|
|
64441
64847
|
});
|
|
64442
64848
|
|
|
64443
64849
|
// ../engine/src/notification/webhook-provider.ts
|
|
64850
|
+
function resolveParticipantLabel2(metadata, kind) {
|
|
64851
|
+
const nameKey = kind === "from" ? "fromName" : "toName";
|
|
64852
|
+
const idKey = kind === "from" ? "fromId" : "toId";
|
|
64853
|
+
const name = typeof metadata?.[nameKey] === "string" ? metadata[nameKey].trim() : "";
|
|
64854
|
+
if (name.length > 0) {
|
|
64855
|
+
return name;
|
|
64856
|
+
}
|
|
64857
|
+
const id = typeof metadata?.[idKey] === "string" ? metadata[idKey].trim() : "";
|
|
64858
|
+
return id.length > 0 ? id : kind === "from" ? "agent" : "recipient";
|
|
64859
|
+
}
|
|
64444
64860
|
var WebhookNotificationProvider;
|
|
64445
64861
|
var init_webhook_provider = __esm({
|
|
64446
64862
|
"../engine/src/notification/webhook-provider.ts"() {
|
|
@@ -64550,6 +64966,17 @@ var init_webhook_provider = __esm({
|
|
|
64550
64966
|
return "Pipeline gridlocked";
|
|
64551
64967
|
case "fallback-used":
|
|
64552
64968
|
return `Fusion recovered by switching from ${String(payload.metadata?.primaryModel ?? "primary model")} to ${String(payload.metadata?.fallbackModel ?? "fallback model")} (${String(payload.metadata?.triggerPoint ?? "unknown trigger")})`;
|
|
64969
|
+
case "message:agent-to-user": {
|
|
64970
|
+
const from = resolveParticipantLabel2(payload.metadata, "from");
|
|
64971
|
+
const preview = typeof payload.metadata?.preview === "string" ? payload.metadata.preview : "(no preview)";
|
|
64972
|
+
return `From: ${from} \u2192 You: ${preview}`;
|
|
64973
|
+
}
|
|
64974
|
+
case "message:agent-to-agent": {
|
|
64975
|
+
const from = resolveParticipantLabel2(payload.metadata, "from");
|
|
64976
|
+
const to = resolveParticipantLabel2(payload.metadata, "to");
|
|
64977
|
+
const preview = typeof payload.metadata?.preview === "string" ? payload.metadata.preview : "(no preview)";
|
|
64978
|
+
return `From: ${from} \u2192 To: ${to}: ${preview}`;
|
|
64979
|
+
}
|
|
64553
64980
|
default:
|
|
64554
64981
|
return `Event "${event}" for task ${identifier}`;
|
|
64555
64982
|
}
|
|
@@ -64573,6 +65000,8 @@ var init_webhook_provider = __esm({
|
|
|
64573
65000
|
return { content: message };
|
|
64574
65001
|
}
|
|
64575
65002
|
const messageId = typeof payload.metadata?.messageId === "string" ? payload.metadata.messageId : void 0;
|
|
65003
|
+
const fromLabel = resolveParticipantLabel2(payload.metadata, "from");
|
|
65004
|
+
const toLabel = resolveParticipantLabel2(payload.metadata, "to");
|
|
64576
65005
|
return {
|
|
64577
65006
|
event: payload.event,
|
|
64578
65007
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -64580,7 +65009,13 @@ var init_webhook_provider = __esm({
|
|
|
64580
65009
|
id: payload.taskId,
|
|
64581
65010
|
title: payload.taskTitle
|
|
64582
65011
|
},
|
|
64583
|
-
metadata:
|
|
65012
|
+
metadata: {
|
|
65013
|
+
...payload.metadata,
|
|
65014
|
+
...payload.event === "message:agent-to-user" || payload.event === "message:agent-to-agent" ? {
|
|
65015
|
+
fromName: typeof payload.metadata?.fromName === "string" ? payload.metadata.fromName : fromLabel,
|
|
65016
|
+
toName: typeof payload.metadata?.toName === "string" ? payload.metadata.toName : toLabel
|
|
65017
|
+
} : {}
|
|
65018
|
+
},
|
|
64584
65019
|
clickUrl: buildNtfyClickUrl({
|
|
64585
65020
|
dashboardHost: this.config.dashboardHost,
|
|
64586
65021
|
projectId: this.config.projectId,
|
|
@@ -64785,6 +65220,8 @@ var init_notification_service = __esm({
|
|
|
64785
65220
|
}
|
|
64786
65221
|
const preview = message.content.length > 100 ? `${message.content.slice(0, 100)}\u2026` : message.content;
|
|
64787
65222
|
const taskId = typeof message.metadata?.taskId === "string" ? message.metadata.taskId : void 0;
|
|
65223
|
+
const fromName = await this.resolveAgentName(message.fromType, message.fromId, "from");
|
|
65224
|
+
const toName = await this.resolveAgentName(message.toType, message.toId, "to");
|
|
64788
65225
|
this.maybeNotify(message.id, eventType, {
|
|
64789
65226
|
taskId,
|
|
64790
65227
|
taskTitle: void 0,
|
|
@@ -64793,8 +65230,10 @@ var init_notification_service = __esm({
|
|
|
64793
65230
|
messageId: message.id,
|
|
64794
65231
|
fromId: message.fromId,
|
|
64795
65232
|
fromType: message.fromType,
|
|
65233
|
+
...fromName ? { fromName } : {},
|
|
64796
65234
|
toId: message.toId,
|
|
64797
65235
|
toType: message.toType,
|
|
65236
|
+
...toName ? { toName } : {},
|
|
64798
65237
|
type: message.type,
|
|
64799
65238
|
replyToMessageId: message.metadata?.replyTo?.messageId,
|
|
64800
65239
|
preview
|
|
@@ -64804,6 +65243,26 @@ var init_notification_service = __esm({
|
|
|
64804
65243
|
`NotificationService.handleMessageSent scheduled eventType=${eventType} messageId=${message.id}`
|
|
64805
65244
|
);
|
|
64806
65245
|
}
|
|
65246
|
+
async resolveAgentName(participantType, participantId, direction) {
|
|
65247
|
+
if (participantType !== "agent") {
|
|
65248
|
+
return null;
|
|
65249
|
+
}
|
|
65250
|
+
const resolver = this.options.agentNameResolver;
|
|
65251
|
+
if (!resolver) {
|
|
65252
|
+
return null;
|
|
65253
|
+
}
|
|
65254
|
+
try {
|
|
65255
|
+
const resolved = await resolver(participantId);
|
|
65256
|
+
const trimmed = typeof resolved === "string" ? resolved.trim() : "";
|
|
65257
|
+
return trimmed.length > 0 ? trimmed : null;
|
|
65258
|
+
} catch (error) {
|
|
65259
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
65260
|
+
schedulerLog.log(
|
|
65261
|
+
`NotificationService.handleMessageSent failed to resolve ${direction} agent name agentId=${participantId} error=${message}`
|
|
65262
|
+
);
|
|
65263
|
+
return null;
|
|
65264
|
+
}
|
|
65265
|
+
}
|
|
64807
65266
|
setNotificationsEnabledFromSettings(settings) {
|
|
64808
65267
|
this.notificationsEnabled = Boolean(
|
|
64809
65268
|
settings.ntfyEnabled && settings.ntfyTopic || settings.webhookEnabled && settings.webhookUrl
|
|
@@ -65006,7 +65465,8 @@ var init_notifier = __esm({
|
|
|
65006
65465
|
this.projectId = options.projectId;
|
|
65007
65466
|
this.notificationService = notificationService ?? new NotificationService(store, {
|
|
65008
65467
|
projectId: this.projectId,
|
|
65009
|
-
ntfyBaseUrl: options.ntfyBaseUrl
|
|
65468
|
+
ntfyBaseUrl: options.ntfyBaseUrl,
|
|
65469
|
+
agentNameResolver: options.agentNameResolver
|
|
65010
65470
|
});
|
|
65011
65471
|
activeNotificationService = this.notificationService;
|
|
65012
65472
|
}
|
|
@@ -65759,20 +66219,20 @@ async function withRateLimitRetry(fn, options = {}) {
|
|
|
65759
66219
|
throw lastError ?? new Error("withRateLimitRetry: unexpected state");
|
|
65760
66220
|
}
|
|
65761
66221
|
function sleep2(ms, signal) {
|
|
65762
|
-
return new Promise((
|
|
66222
|
+
return new Promise((resolve24, reject) => {
|
|
65763
66223
|
if (signal?.aborted) {
|
|
65764
66224
|
reject(signal.reason ?? new Error("Aborted"));
|
|
65765
66225
|
return;
|
|
65766
66226
|
}
|
|
65767
|
-
const timer = setTimeout(
|
|
66227
|
+
const timer = setTimeout(resolve24, ms);
|
|
65768
66228
|
if (signal) {
|
|
65769
66229
|
const onAbort = () => {
|
|
65770
66230
|
clearTimeout(timer);
|
|
65771
66231
|
reject(signal.reason ?? new Error("Aborted"));
|
|
65772
66232
|
};
|
|
65773
66233
|
signal.addEventListener("abort", onAbort, { once: true });
|
|
65774
|
-
const origResolve =
|
|
65775
|
-
|
|
66234
|
+
const origResolve = resolve24;
|
|
66235
|
+
resolve24 = () => {
|
|
65776
66236
|
signal.removeEventListener("abort", onAbort);
|
|
65777
66237
|
origResolve();
|
|
65778
66238
|
};
|
|
@@ -65861,8 +66321,8 @@ If research is disabled or providers are not configured, use the actionable tool
|
|
|
65861
66321
|
|
|
65862
66322
|
// ../engine/src/triage.ts
|
|
65863
66323
|
import { Type as Type2 } from "@mariozechner/pi-ai";
|
|
65864
|
-
import { readFile as
|
|
65865
|
-
import { join as
|
|
66324
|
+
import { readFile as readFile16 } from "node:fs/promises";
|
|
66325
|
+
import { join as join31 } from "node:path";
|
|
65866
66326
|
function extractPromptDeclaredTitle(prompt, taskId) {
|
|
65867
66327
|
const headingMatch = prompt.match(/^#\s+Task:\s+([A-Z]+-\d+)\s+-\s+(.+)$/m);
|
|
65868
66328
|
if (!headingMatch) return null;
|
|
@@ -65890,10 +66350,10 @@ async function readAttachmentContents(rootDir, taskId, attachments) {
|
|
|
65890
66350
|
if (!attachments || attachments.length === 0) {
|
|
65891
66351
|
return { attachmentContents, imageContents };
|
|
65892
66352
|
}
|
|
65893
|
-
const { readFile:
|
|
65894
|
-
const { join:
|
|
66353
|
+
const { readFile: readFile23 } = await import("node:fs/promises");
|
|
66354
|
+
const { join: join47 } = await import("node:path");
|
|
65895
66355
|
for (const att of attachments) {
|
|
65896
|
-
const filePath =
|
|
66356
|
+
const filePath = join47(
|
|
65897
66357
|
rootDir,
|
|
65898
66358
|
".fusion",
|
|
65899
66359
|
"tasks",
|
|
@@ -65903,7 +66363,7 @@ async function readAttachmentContents(rootDir, taskId, attachments) {
|
|
|
65903
66363
|
);
|
|
65904
66364
|
try {
|
|
65905
66365
|
if (IMAGE_MIME_TYPES.has(att.mimeType)) {
|
|
65906
|
-
const data = await
|
|
66366
|
+
const data = await readFile23(filePath);
|
|
65907
66367
|
imageContents.push({
|
|
65908
66368
|
type: "image",
|
|
65909
66369
|
data: data.toString("base64"),
|
|
@@ -65915,7 +66375,7 @@ async function readAttachmentContents(rootDir, taskId, attachments) {
|
|
|
65915
66375
|
text: null
|
|
65916
66376
|
});
|
|
65917
66377
|
} else {
|
|
65918
|
-
const data = await
|
|
66378
|
+
const data = await readFile23(filePath, "utf-8");
|
|
65919
66379
|
const text = data.length > TEXT_INLINE_LIMIT ? data.slice(0, TEXT_INLINE_LIMIT) + "\n... (truncated at 50KB)" : data;
|
|
65920
66380
|
attachmentContents.push({
|
|
65921
66381
|
originalName: att.originalName,
|
|
@@ -66736,8 +67196,8 @@ Write the PROMPT.md directly using the write tool, then call \`fn_review_spec()\
|
|
|
66736
67196
|
return false;
|
|
66737
67197
|
}
|
|
66738
67198
|
const settings = await this.store.getSettings();
|
|
66739
|
-
const promptPath =
|
|
66740
|
-
const written = await
|
|
67199
|
+
const promptPath = join31(this.rootDir, ".fusion", "tasks", task.id, "PROMPT.md");
|
|
67200
|
+
const written = await readFile16(promptPath, "utf-8").catch((err) => {
|
|
66741
67201
|
const msg = err instanceof Error ? err.message : String(err);
|
|
66742
67202
|
planLog.warn(`${task.id}: failed to read PROMPT.md during approved-spec recovery (${promptPath}): ${msg}`);
|
|
66743
67203
|
return "";
|
|
@@ -67244,8 +67704,8 @@ ${triagePluginContributions}` : triageSystemPrompt;
|
|
|
67244
67704
|
await this.store.updateTask(task.id, { status: "needs-replan" });
|
|
67245
67705
|
return;
|
|
67246
67706
|
}
|
|
67247
|
-
const written = await
|
|
67248
|
-
|
|
67707
|
+
const written = await readFile16(
|
|
67708
|
+
join31(this.rootDir, promptPath),
|
|
67249
67709
|
"utf-8"
|
|
67250
67710
|
).catch((err) => {
|
|
67251
67711
|
const msg = err instanceof Error ? err.message : String(err);
|
|
@@ -67563,10 +68023,10 @@ Remove or replace these ids and call fn_task_create again.`
|
|
|
67563
68023
|
checkpointRef.current = sessionRef.current.sessionManager.getLeafId() ?? null;
|
|
67564
68024
|
}
|
|
67565
68025
|
try {
|
|
67566
|
-
const { readFile:
|
|
67567
|
-
const { join:
|
|
67568
|
-
const promptContent = await
|
|
67569
|
-
|
|
68026
|
+
const { readFile: readFile23 } = await import("node:fs/promises");
|
|
68027
|
+
const { join: join47 } = await import("node:path");
|
|
68028
|
+
const promptContent = await readFile23(
|
|
68029
|
+
join47(rootDir, promptPath),
|
|
67570
68030
|
"utf-8"
|
|
67571
68031
|
).catch((err) => {
|
|
67572
68032
|
const msg = err instanceof Error ? err.message : String(err);
|
|
@@ -67797,7 +68257,7 @@ Take a completely different approach to writing this specification. Do NOT repea
|
|
|
67797
68257
|
// ../engine/src/verification-utils.ts
|
|
67798
68258
|
import { spawn as spawn3 } from "node:child_process";
|
|
67799
68259
|
async function execWithProcessGroup(command, options) {
|
|
67800
|
-
return new Promise((
|
|
68260
|
+
return new Promise((resolve24, reject) => {
|
|
67801
68261
|
if (options.signal?.aborted) {
|
|
67802
68262
|
reject(Object.assign(
|
|
67803
68263
|
new Error(`Command aborted before start: ${command}`),
|
|
@@ -67811,7 +68271,13 @@ async function execWithProcessGroup(command, options) {
|
|
|
67811
68271
|
shell: true,
|
|
67812
68272
|
detached: useProcessGroup,
|
|
67813
68273
|
stdio: ["ignore", "pipe", "pipe"],
|
|
67814
|
-
|
|
68274
|
+
env: {
|
|
68275
|
+
...process.env,
|
|
68276
|
+
// Corepack otherwise prompts interactively before fetching a pinned
|
|
68277
|
+
// packageManager version, hanging the non-TTY child until timeout.
|
|
68278
|
+
COREPACK_ENABLE_DOWNLOAD_PROMPT: "0",
|
|
68279
|
+
...options.env ?? {}
|
|
68280
|
+
}
|
|
67815
68281
|
});
|
|
67816
68282
|
let stdout = "";
|
|
67817
68283
|
let stderr = "";
|
|
@@ -67891,7 +68357,7 @@ async function execWithProcessGroup(command, options) {
|
|
|
67891
68357
|
return;
|
|
67892
68358
|
}
|
|
67893
68359
|
if (code === 0) {
|
|
67894
|
-
|
|
68360
|
+
resolve24({ stdout, stderr, bufferOverflow: stdoutOverflow || stderrOverflow });
|
|
67895
68361
|
return;
|
|
67896
68362
|
}
|
|
67897
68363
|
reject(Object.assign(
|
|
@@ -68286,9 +68752,9 @@ var init_run_audit = __esm({
|
|
|
68286
68752
|
// ../engine/src/merger.ts
|
|
68287
68753
|
import { execSync, exec as exec3, execFile as execFile3 } from "node:child_process";
|
|
68288
68754
|
import { promisify as promisify4 } from "node:util";
|
|
68289
|
-
import { existsSync as
|
|
68755
|
+
import { existsSync as existsSync25, readFileSync as readFileSync11, writeFileSync as writeFileSync2, unlinkSync, renameSync as renameSync2 } from "node:fs";
|
|
68290
68756
|
import { createHash as createHash6 } from "node:crypto";
|
|
68291
|
-
import { join as
|
|
68757
|
+
import { join as join32 } from "node:path";
|
|
68292
68758
|
import { hostname } from "node:os";
|
|
68293
68759
|
import { Type as Type3 } from "typebox";
|
|
68294
68760
|
function truncateWorkflowScriptOutput(output) {
|
|
@@ -68334,7 +68800,7 @@ async function getStagedFiles(cwd) {
|
|
|
68334
68800
|
}
|
|
68335
68801
|
}
|
|
68336
68802
|
function hasInstallState(rootDir) {
|
|
68337
|
-
return
|
|
68803
|
+
return existsSync25(join32(rootDir, "node_modules")) || existsSync25(join32(rootDir, ".pnp.cjs"));
|
|
68338
68804
|
}
|
|
68339
68805
|
function shouldSyncDependenciesForMerge(stagedFiles, installStatePresent) {
|
|
68340
68806
|
if (!installStatePresent) return true;
|
|
@@ -68343,18 +68809,18 @@ function shouldSyncDependenciesForMerge(stagedFiles, installStatePresent) {
|
|
|
68343
68809
|
);
|
|
68344
68810
|
}
|
|
68345
68811
|
function getDependencySyncCommand(rootDir) {
|
|
68346
|
-
if (
|
|
68347
|
-
if (
|
|
68348
|
-
if (
|
|
68349
|
-
if (
|
|
68812
|
+
if (existsSync25(join32(rootDir, "pnpm-lock.yaml"))) return "pnpm install --frozen-lockfile";
|
|
68813
|
+
if (existsSync25(join32(rootDir, "package-lock.json"))) return "npm install";
|
|
68814
|
+
if (existsSync25(join32(rootDir, "yarn.lock"))) return "yarn install --frozen-lockfile";
|
|
68815
|
+
if (existsSync25(join32(rootDir, "bun.lock")) || existsSync25(join32(rootDir, "bun.lockb"))) {
|
|
68350
68816
|
return "bun install --frozen-lockfile";
|
|
68351
68817
|
}
|
|
68352
68818
|
return null;
|
|
68353
68819
|
}
|
|
68354
68820
|
function computeLockfileHash(rootDir) {
|
|
68355
68821
|
for (const name of LOCKFILE_CANDIDATES) {
|
|
68356
|
-
const p =
|
|
68357
|
-
if (
|
|
68822
|
+
const p = join32(rootDir, name);
|
|
68823
|
+
if (existsSync25(p)) {
|
|
68358
68824
|
try {
|
|
68359
68825
|
return createHash6("sha256").update(readFileSync11(p)).digest("hex");
|
|
68360
68826
|
} catch {
|
|
@@ -68366,7 +68832,7 @@ function computeLockfileHash(rootDir) {
|
|
|
68366
68832
|
}
|
|
68367
68833
|
function readInstallMarker(rootDir) {
|
|
68368
68834
|
try {
|
|
68369
|
-
const value = readFileSync11(
|
|
68835
|
+
const value = readFileSync11(join32(rootDir, INSTALL_MARKER_RELPATH), "utf-8").trim();
|
|
68370
68836
|
return value || null;
|
|
68371
68837
|
} catch {
|
|
68372
68838
|
return null;
|
|
@@ -68374,7 +68840,7 @@ function readInstallMarker(rootDir) {
|
|
|
68374
68840
|
}
|
|
68375
68841
|
function writeInstallMarker(rootDir, hash) {
|
|
68376
68842
|
try {
|
|
68377
|
-
writeFileSync2(
|
|
68843
|
+
writeFileSync2(join32(rootDir, INSTALL_MARKER_RELPATH), hash);
|
|
68378
68844
|
} catch {
|
|
68379
68845
|
}
|
|
68380
68846
|
}
|
|
@@ -68466,8 +68932,8 @@ function inferDefaultTestCommand(rootDir, explicitTestCommand, explicitBuildComm
|
|
|
68466
68932
|
buildSource: explicitBuildCommand?.trim() ? "explicit" : void 0
|
|
68467
68933
|
};
|
|
68468
68934
|
}
|
|
68469
|
-
if (
|
|
68470
|
-
if (
|
|
68935
|
+
if (existsSync25(join32(rootDir, "pnpm-lock.yaml"))) {
|
|
68936
|
+
if (existsSync25(join32(rootDir, "pnpm-workspace.yaml"))) {
|
|
68471
68937
|
mergerLog.warn(
|
|
68472
68938
|
`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\`.`
|
|
68473
68939
|
);
|
|
@@ -68478,21 +68944,21 @@ function inferDefaultTestCommand(rootDir, explicitTestCommand, explicitBuildComm
|
|
|
68478
68944
|
buildSource: explicitBuildCommand?.trim() ? "explicit" : void 0
|
|
68479
68945
|
};
|
|
68480
68946
|
}
|
|
68481
|
-
if (
|
|
68947
|
+
if (existsSync25(join32(rootDir, "yarn.lock"))) {
|
|
68482
68948
|
return {
|
|
68483
68949
|
command: "yarn test",
|
|
68484
68950
|
testSource: "inferred",
|
|
68485
68951
|
buildSource: explicitBuildCommand?.trim() ? "explicit" : void 0
|
|
68486
68952
|
};
|
|
68487
68953
|
}
|
|
68488
|
-
if (
|
|
68954
|
+
if (existsSync25(join32(rootDir, "bun.lock")) || existsSync25(join32(rootDir, "bun.lockb"))) {
|
|
68489
68955
|
return {
|
|
68490
68956
|
command: "bun test",
|
|
68491
68957
|
testSource: "inferred",
|
|
68492
68958
|
buildSource: explicitBuildCommand?.trim() ? "explicit" : void 0
|
|
68493
68959
|
};
|
|
68494
68960
|
}
|
|
68495
|
-
if (
|
|
68961
|
+
if (existsSync25(join32(rootDir, "package-lock.json"))) {
|
|
68496
68962
|
return {
|
|
68497
68963
|
command: "npm test",
|
|
68498
68964
|
testSource: "inferred",
|
|
@@ -68931,7 +69397,7 @@ async function stashTreesEqual(rootDir, aSha, bSha) {
|
|
|
68931
69397
|
}
|
|
68932
69398
|
function writeActiveMergerStatus(rootDir, taskId) {
|
|
68933
69399
|
try {
|
|
68934
|
-
const statusPath =
|
|
69400
|
+
const statusPath = join32(rootDir, ".git", ACTIVE_MERGER_STATUS_FILENAME);
|
|
68935
69401
|
const payload = {
|
|
68936
69402
|
taskId,
|
|
68937
69403
|
pid: process.pid,
|
|
@@ -69362,8 +69828,8 @@ async function findFilesWithConflictMarkers(rootDir, files) {
|
|
|
69362
69828
|
const stillConflicted = [];
|
|
69363
69829
|
for (const file of files) {
|
|
69364
69830
|
try {
|
|
69365
|
-
const fullPath =
|
|
69366
|
-
if (!
|
|
69831
|
+
const fullPath = join32(rootDir, file);
|
|
69832
|
+
if (!existsSync25(fullPath)) continue;
|
|
69367
69833
|
const { stdout } = await execAsync2(
|
|
69368
69834
|
`git grep -l -e "^<<<<<<< " -e "^=======$" -e "^>>>>>>> " --no-index -- ${quoteArg(fullPath)}`,
|
|
69369
69835
|
{ cwd: rootDir, encoding: "utf-8" }
|
|
@@ -69389,7 +69855,7 @@ async function tryRecoverHardFailApply(params) {
|
|
|
69389
69855
|
if (!patchText.trim()) {
|
|
69390
69856
|
mergerLog.warn(`${taskId}: autostash ${sha.slice(0, 7)} produced empty patch; cannot 3-way recover`);
|
|
69391
69857
|
} else {
|
|
69392
|
-
const patchPath =
|
|
69858
|
+
const patchPath = join32(rootDir, ".git", `fusion-autostash-${sha.slice(0, 7)}.patch`);
|
|
69393
69859
|
writeFileSync2(patchPath, patchText, "utf-8");
|
|
69394
69860
|
try {
|
|
69395
69861
|
await execAsync2(`git apply --3way --whitespace=nowarn ${quoteArg(patchPath)}`, { cwd: rootDir });
|
|
@@ -70226,16 +70692,46 @@ async function resolveTaskDiffBaseRef({
|
|
|
70226
70692
|
return mergeBase;
|
|
70227
70693
|
}
|
|
70228
70694
|
}
|
|
70695
|
+
let recoveredBase;
|
|
70696
|
+
if (!baseBranch?.trim()) {
|
|
70697
|
+
try {
|
|
70698
|
+
const { stdout } = await execAsync2(`git merge-base ${quotedHeadRef} main`, {
|
|
70699
|
+
cwd,
|
|
70700
|
+
encoding: "utf-8"
|
|
70701
|
+
});
|
|
70702
|
+
recoveredBase = stdout.trim() || void 0;
|
|
70703
|
+
} catch {
|
|
70704
|
+
try {
|
|
70705
|
+
const { stdout } = await execAsync2(`git merge-base ${quotedHeadRef} ${quoteArg("origin/main")}`, {
|
|
70706
|
+
cwd,
|
|
70707
|
+
encoding: "utf-8"
|
|
70708
|
+
});
|
|
70709
|
+
recoveredBase = stdout.trim() || void 0;
|
|
70710
|
+
} catch {
|
|
70711
|
+
}
|
|
70712
|
+
}
|
|
70713
|
+
}
|
|
70229
70714
|
if (baseCommitSha) {
|
|
70230
70715
|
try {
|
|
70231
70716
|
await execAsync2(`git merge-base --is-ancestor ${quoteArg(baseCommitSha)} ${quotedHeadRef}`, {
|
|
70232
70717
|
cwd,
|
|
70233
70718
|
encoding: "utf-8"
|
|
70234
70719
|
});
|
|
70720
|
+
if (recoveredBase && recoveredBase !== baseCommitSha) {
|
|
70721
|
+
try {
|
|
70722
|
+
await execAsync2(`git merge-base --is-ancestor ${quoteArg(baseCommitSha)} ${quoteArg(recoveredBase)}`, {
|
|
70723
|
+
cwd,
|
|
70724
|
+
encoding: "utf-8"
|
|
70725
|
+
});
|
|
70726
|
+
return recoveredBase;
|
|
70727
|
+
} catch {
|
|
70728
|
+
}
|
|
70729
|
+
}
|
|
70235
70730
|
return baseCommitSha;
|
|
70236
70731
|
} catch {
|
|
70237
70732
|
}
|
|
70238
70733
|
}
|
|
70734
|
+
if (recoveredBase) return recoveredBase;
|
|
70239
70735
|
try {
|
|
70240
70736
|
const { stdout } = await execAsync2(`git rev-parse ${quoteArg(`${headRef}~1`)}`, {
|
|
70241
70737
|
cwd,
|
|
@@ -70882,7 +71378,7 @@ async function pushToRemoteAfterMerge(store, rootDir, taskId, settings, options)
|
|
|
70882
71378
|
}
|
|
70883
71379
|
async function createPostMergeWorktree(rootDir, taskId) {
|
|
70884
71380
|
const randomSuffix = Math.random().toString(36).slice(2, 10);
|
|
70885
|
-
const postMergeWorktree =
|
|
71381
|
+
const postMergeWorktree = join32(rootDir, ".worktrees", `post-merge-${taskId}-${randomSuffix}`);
|
|
70886
71382
|
try {
|
|
70887
71383
|
await execAsync2(`git worktree add ${quoteArg(postMergeWorktree)} HEAD`, { cwd: rootDir });
|
|
70888
71384
|
return postMergeWorktree;
|
|
@@ -71460,7 +71956,7 @@ async function aiMergeTask(store, rootDir, taskId, options = {}) {
|
|
|
71460
71956
|
}
|
|
71461
71957
|
if (error.name === "VerificationError") {
|
|
71462
71958
|
const verificationErr = error;
|
|
71463
|
-
const maxFixRetries = Math.min(settings.verificationFixRetries ??
|
|
71959
|
+
const maxFixRetries = Math.min(settings.verificationFixRetries ?? 2, 3);
|
|
71464
71960
|
if (maxFixRetries > 0 && (verificationErr.verificationResult.testResult || verificationErr.verificationResult.buildResult)) {
|
|
71465
71961
|
mergerLog.log(`${taskId}: deterministic verification failed \u2014 attempting in-merge fix (up to ${maxFixRetries} attempts)`);
|
|
71466
71962
|
await store.logEntry(taskId, `Verification failed during merge \u2014 attempting in-merge fix (up to ${maxFixRetries} attempts)`);
|
|
@@ -71568,7 +72064,7 @@ async function aiMergeTask(store, rootDir, taskId, options = {}) {
|
|
|
71568
72064
|
throw error;
|
|
71569
72065
|
}
|
|
71570
72066
|
if (error.message?.includes("Build verification failed")) {
|
|
71571
|
-
const maxFixRetries = Math.min(settings.verificationFixRetries ??
|
|
72067
|
+
const maxFixRetries = Math.min(settings.verificationFixRetries ?? 2, 3);
|
|
71572
72068
|
if (maxFixRetries > 0 && (effectiveTestCommand || effectiveBuildCommand)) {
|
|
71573
72069
|
mergerLog.log(`${taskId}: build verification failed \u2014 attempting in-merge fix`);
|
|
71574
72070
|
await store.logEntry(taskId, `Build verification failed during merge \u2014 attempting in-merge fix`);
|
|
@@ -71857,7 +72353,7 @@ async function aiMergeTask(store, rootDir, taskId, options = {}) {
|
|
|
71857
72353
|
}
|
|
71858
72354
|
}
|
|
71859
72355
|
throwIfAborted(options.signal, taskId);
|
|
71860
|
-
if (worktreePath &&
|
|
72356
|
+
if (worktreePath && existsSync25(worktreePath)) {
|
|
71861
72357
|
const otherUser = await findWorktreeUser(store, worktreePath, taskId);
|
|
71862
72358
|
if (otherUser) {
|
|
71863
72359
|
mergerLog.log(`Worktree retained \u2014 still needed by ${otherUser}`);
|
|
@@ -73092,7 +73588,7 @@ var init_merger = __esm({
|
|
|
73092
73588
|
PUSH_TIMEOUT_MS = 6e4;
|
|
73093
73589
|
MERGE_COMMIT_LOG_MAX_CHARS = 5e3;
|
|
73094
73590
|
MERGE_DIFF_STAT_MAX_CHARS = 3e3;
|
|
73095
|
-
INSTALL_MARKER_RELPATH =
|
|
73591
|
+
INSTALL_MARKER_RELPATH = join32("node_modules", ".fusion-install-marker");
|
|
73096
73592
|
LOCKFILE_CANDIDATES = ["pnpm-lock.yaml", "package-lock.json", "yarn.lock", "bun.lockb", "bun.lock"];
|
|
73097
73593
|
VerificationError = class extends Error {
|
|
73098
73594
|
constructor(message, verificationResult) {
|
|
@@ -73122,8 +73618,8 @@ var init_merger = __esm({
|
|
|
73122
73618
|
|
|
73123
73619
|
// ../engine/src/worktree-names.ts
|
|
73124
73620
|
import { readdirSync as readdirSync3 } from "node:fs";
|
|
73125
|
-
import { join as
|
|
73126
|
-
import { existsSync as
|
|
73621
|
+
import { join as join33 } from "node:path";
|
|
73622
|
+
import { existsSync as existsSync26 } from "node:fs";
|
|
73127
73623
|
function slugify2(str) {
|
|
73128
73624
|
return str.toLowerCase().replace(/[^a-z0-9\s-]/g, "").replace(/\s+/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "");
|
|
73129
73625
|
}
|
|
@@ -73134,7 +73630,7 @@ function generateReservedWorktreeName(rootDir, reservedNames = /* @__PURE__ */ n
|
|
|
73134
73630
|
const adjective = ADJECTIVES[Math.floor(Math.random() * ADJECTIVES.length)];
|
|
73135
73631
|
const noun = NOUNS[Math.floor(Math.random() * NOUNS.length)];
|
|
73136
73632
|
const baseName = `${adjective}-${noun}`;
|
|
73137
|
-
const worktreesDir =
|
|
73633
|
+
const worktreesDir = join33(rootDir, ".worktrees");
|
|
73138
73634
|
const existing = getExistingWorktreeNames(worktreesDir);
|
|
73139
73635
|
for (const reserved of reservedNames) {
|
|
73140
73636
|
existing.add(reserved);
|
|
@@ -73168,10 +73664,10 @@ function planTaskWorktreePath(task, rootDir, naming, reservedNames) {
|
|
|
73168
73664
|
break;
|
|
73169
73665
|
}
|
|
73170
73666
|
reservedNames.add(worktreeName);
|
|
73171
|
-
return
|
|
73667
|
+
return join33(rootDir, ".worktrees", worktreeName);
|
|
73172
73668
|
}
|
|
73173
73669
|
function getExistingWorktreeNames(worktreesDir) {
|
|
73174
|
-
if (!
|
|
73670
|
+
if (!existsSync26(worktreesDir)) {
|
|
73175
73671
|
return /* @__PURE__ */ new Set();
|
|
73176
73672
|
}
|
|
73177
73673
|
try {
|
|
@@ -73308,8 +73804,8 @@ __export(worktree_pool_exports, {
|
|
|
73308
73804
|
});
|
|
73309
73805
|
import { exec as exec4 } from "node:child_process";
|
|
73310
73806
|
import { promisify as promisify5 } from "node:util";
|
|
73311
|
-
import { existsSync as
|
|
73312
|
-
import { join as
|
|
73807
|
+
import { existsSync as existsSync27, lstatSync, readdirSync as readdirSync4, rmSync as rmSync2 } from "node:fs";
|
|
73808
|
+
import { join as join34, relative as relative8, resolve as resolve18, isAbsolute as isAbsolute10 } from "node:path";
|
|
73313
73809
|
function getExecStdout(result) {
|
|
73314
73810
|
if (typeof result === "string") return result;
|
|
73315
73811
|
if (result && typeof result === "object" && "stdout" in result) {
|
|
@@ -73341,7 +73837,7 @@ async function getRegisteredWorktreePaths(rootDir) {
|
|
|
73341
73837
|
const paths = /* @__PURE__ */ new Set();
|
|
73342
73838
|
for (const line of stdout.split("\n")) {
|
|
73343
73839
|
if (line.startsWith("worktree ")) {
|
|
73344
|
-
paths.add(
|
|
73840
|
+
paths.add(resolve18(line.slice("worktree ".length)));
|
|
73345
73841
|
}
|
|
73346
73842
|
}
|
|
73347
73843
|
return paths;
|
|
@@ -73352,29 +73848,29 @@ async function getRegisteredWorktreePaths(rootDir) {
|
|
|
73352
73848
|
}
|
|
73353
73849
|
}
|
|
73354
73850
|
async function isRegisteredGitWorktree2(rootDir, worktreePath) {
|
|
73355
|
-
return (await getRegisteredWorktreePaths(rootDir)).has(
|
|
73851
|
+
return (await getRegisteredWorktreePaths(rootDir)).has(resolve18(worktreePath));
|
|
73356
73852
|
}
|
|
73357
73853
|
function hasRequiredWorktreeFiles(worktreePath) {
|
|
73358
|
-
return
|
|
73854
|
+
return existsSync27(join34(worktreePath, ".git")) && existsSync27(join34(worktreePath, "package.json"));
|
|
73359
73855
|
}
|
|
73360
73856
|
async function isUsableTaskWorktree(rootDir, worktreePath) {
|
|
73361
|
-
return
|
|
73857
|
+
return existsSync27(worktreePath) && await isRegisteredGitWorktree2(rootDir, worktreePath) && hasRequiredWorktreeFiles(worktreePath);
|
|
73362
73858
|
}
|
|
73363
73859
|
function isInsideWorktreesDir(rootDir, worktreePath) {
|
|
73364
|
-
const worktreesDir =
|
|
73365
|
-
const target =
|
|
73366
|
-
const rel =
|
|
73367
|
-
return rel !== "" && !rel.startsWith("..") && !
|
|
73860
|
+
const worktreesDir = resolve18(rootDir, ".worktrees");
|
|
73861
|
+
const target = resolve18(worktreePath);
|
|
73862
|
+
const rel = relative8(worktreesDir, target);
|
|
73863
|
+
return rel !== "" && !rel.startsWith("..") && !isAbsolute10(rel);
|
|
73368
73864
|
}
|
|
73369
73865
|
async function scanIdleWorktrees(rootDir, store) {
|
|
73370
|
-
const worktreesDir =
|
|
73371
|
-
if (!
|
|
73866
|
+
const worktreesDir = join34(rootDir, ".worktrees");
|
|
73867
|
+
if (!existsSync27(worktreesDir)) {
|
|
73372
73868
|
return [];
|
|
73373
73869
|
}
|
|
73374
73870
|
let dirs;
|
|
73375
73871
|
try {
|
|
73376
73872
|
const entries = readdirSync4(worktreesDir, { withFileTypes: true });
|
|
73377
|
-
dirs = entries.filter((e) => e.isDirectory()).map((e) =>
|
|
73873
|
+
dirs = entries.filter((e) => e.isDirectory()).map((e) => join34(worktreesDir, e.name));
|
|
73378
73874
|
} catch (err) {
|
|
73379
73875
|
const errorMessage = err instanceof Error ? err.message : String(err);
|
|
73380
73876
|
worktreePoolLog.warn(`Failed to read .worktrees/ directory: ${errorMessage}`);
|
|
@@ -73384,41 +73880,41 @@ async function scanIdleWorktrees(rootDir, store) {
|
|
|
73384
73880
|
return [];
|
|
73385
73881
|
}
|
|
73386
73882
|
const registeredWorktrees = await getRegisteredWorktreePaths(rootDir);
|
|
73387
|
-
const registeredDirs = dirs.filter((dir) => registeredWorktrees.has(
|
|
73883
|
+
const registeredDirs = dirs.filter((dir) => registeredWorktrees.has(resolve18(dir)));
|
|
73388
73884
|
const tasks = await store.listTasks({ slim: true, includeArchived: false });
|
|
73389
73885
|
const activeWorktrees = /* @__PURE__ */ new Set();
|
|
73390
73886
|
for (const task of tasks) {
|
|
73391
|
-
if (task.worktree && task.column !== "done" && registeredWorktrees.has(
|
|
73392
|
-
activeWorktrees.add(
|
|
73887
|
+
if (task.worktree && task.column !== "done" && registeredWorktrees.has(resolve18(task.worktree))) {
|
|
73888
|
+
activeWorktrees.add(resolve18(task.worktree));
|
|
73393
73889
|
} else if (task.worktree && task.column !== "done") {
|
|
73394
73890
|
worktreePoolLog.log(`Ignoring task ${task.id} worktree metadata because it is not a registered git worktree: ${task.worktree}`);
|
|
73395
73891
|
}
|
|
73396
73892
|
}
|
|
73397
|
-
return registeredDirs.filter((dir) => !activeWorktrees.has(
|
|
73893
|
+
return registeredDirs.filter((dir) => !activeWorktrees.has(resolve18(dir)));
|
|
73398
73894
|
}
|
|
73399
73895
|
async function cleanupOrphanedWorktrees(rootDir, store) {
|
|
73400
|
-
const worktreesDir =
|
|
73401
|
-
if (!
|
|
73896
|
+
const worktreesDir = join34(rootDir, ".worktrees");
|
|
73897
|
+
if (!existsSync27(worktreesDir)) {
|
|
73402
73898
|
return 0;
|
|
73403
73899
|
}
|
|
73404
73900
|
const orphaned = await scanIdleWorktrees(rootDir, store);
|
|
73405
73901
|
const registeredWorktrees = await getRegisteredWorktreePaths(rootDir);
|
|
73406
73902
|
let dirs = [];
|
|
73407
|
-
if (
|
|
73903
|
+
if (existsSync27(worktreesDir)) {
|
|
73408
73904
|
try {
|
|
73409
|
-
dirs = readdirSync4(worktreesDir, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) =>
|
|
73905
|
+
dirs = readdirSync4(worktreesDir, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => join34(worktreesDir, e.name));
|
|
73410
73906
|
} catch (err) {
|
|
73411
73907
|
const errorMessage = err instanceof Error ? err.message : String(err);
|
|
73412
73908
|
worktreePoolLog.warn(`Failed to read .worktrees/ directory for cleanup: ${errorMessage}`);
|
|
73413
73909
|
dirs = [];
|
|
73414
73910
|
}
|
|
73415
73911
|
}
|
|
73416
|
-
const unregistered = dirs.filter((dir) => !registeredWorktrees.has(
|
|
73912
|
+
const unregistered = dirs.filter((dir) => !registeredWorktrees.has(resolve18(dir)));
|
|
73417
73913
|
const candidates = [...orphaned, ...unregistered];
|
|
73418
73914
|
let cleaned = 0;
|
|
73419
73915
|
for (const worktreePath of candidates) {
|
|
73420
73916
|
try {
|
|
73421
|
-
if (registeredWorktrees.has(
|
|
73917
|
+
if (registeredWorktrees.has(resolve18(worktreePath))) {
|
|
73422
73918
|
await execAsync3(`git worktree remove "${worktreePath}" --force`, {
|
|
73423
73919
|
cwd: rootDir
|
|
73424
73920
|
});
|
|
@@ -73438,8 +73934,8 @@ async function cleanupOrphanedWorktrees(rootDir, store) {
|
|
|
73438
73934
|
return cleaned;
|
|
73439
73935
|
}
|
|
73440
73936
|
async function reapOrphanWorktrees(projectRoot) {
|
|
73441
|
-
const worktreesDir =
|
|
73442
|
-
if (!
|
|
73937
|
+
const worktreesDir = join34(projectRoot, ".worktrees");
|
|
73938
|
+
if (!existsSync27(worktreesDir)) {
|
|
73443
73939
|
return 0;
|
|
73444
73940
|
}
|
|
73445
73941
|
let entries;
|
|
@@ -73447,11 +73943,11 @@ async function reapOrphanWorktrees(projectRoot) {
|
|
|
73447
73943
|
entries = readdirSync4(worktreesDir, { withFileTypes: true }).filter((e) => {
|
|
73448
73944
|
if (!e.isDirectory()) return false;
|
|
73449
73945
|
try {
|
|
73450
|
-
return lstatSync(
|
|
73946
|
+
return lstatSync(join34(worktreesDir, e.name)).isDirectory() && !lstatSync(join34(worktreesDir, e.name)).isSymbolicLink();
|
|
73451
73947
|
} catch {
|
|
73452
73948
|
return false;
|
|
73453
73949
|
}
|
|
73454
|
-
}).map((e) => ({ name: e.name, fullPath:
|
|
73950
|
+
}).map((e) => ({ name: e.name, fullPath: join34(worktreesDir, e.name) }));
|
|
73455
73951
|
} catch (err) {
|
|
73456
73952
|
const msg = err instanceof Error ? err.message : String(err);
|
|
73457
73953
|
worktreePoolLog.warn(`reapOrphanWorktrees: failed to read .worktrees/ \u2014 ${msg}`);
|
|
@@ -73461,17 +73957,17 @@ async function reapOrphanWorktrees(projectRoot) {
|
|
|
73461
73957
|
const registered = await getRegisteredWorktreePaths(projectRoot);
|
|
73462
73958
|
let removed = 0;
|
|
73463
73959
|
for (const { name, fullPath } of entries) {
|
|
73464
|
-
const resolvedFull =
|
|
73465
|
-
const rel =
|
|
73466
|
-
if (!rel || rel.startsWith("..") ||
|
|
73960
|
+
const resolvedFull = resolve18(fullPath);
|
|
73961
|
+
const rel = relative8(resolve18(worktreesDir), resolvedFull);
|
|
73962
|
+
if (!rel || rel.startsWith("..") || isAbsolute10(rel)) {
|
|
73467
73963
|
worktreePoolLog.warn(`reapOrphanWorktrees: skipping out-of-bounds path ${fullPath}`);
|
|
73468
73964
|
continue;
|
|
73469
73965
|
}
|
|
73470
73966
|
if (registered.has(resolvedFull)) {
|
|
73471
73967
|
continue;
|
|
73472
73968
|
}
|
|
73473
|
-
const dotGit =
|
|
73474
|
-
if (
|
|
73969
|
+
const dotGit = join34(resolvedFull, ".git");
|
|
73970
|
+
if (existsSync27(dotGit)) {
|
|
73475
73971
|
worktreePoolLog.log(`reapOrphanWorktrees: skipping ${name} (has .git entry but not in registered list \u2014 may be partially registered)`);
|
|
73476
73972
|
continue;
|
|
73477
73973
|
}
|
|
@@ -73531,7 +74027,7 @@ var init_worktree_pool = __esm({
|
|
|
73531
74027
|
acquire() {
|
|
73532
74028
|
for (const path2 of this.idle) {
|
|
73533
74029
|
this.idle.delete(path2);
|
|
73534
|
-
if (
|
|
74030
|
+
if (existsSync27(path2)) {
|
|
73535
74031
|
return path2;
|
|
73536
74032
|
}
|
|
73537
74033
|
worktreePoolLog.log(`Pruned stale entry: ${path2}`);
|
|
@@ -73578,7 +74074,7 @@ var init_worktree_pool = __esm({
|
|
|
73578
74074
|
*/
|
|
73579
74075
|
rehydrate(idlePaths) {
|
|
73580
74076
|
for (const path2 of idlePaths) {
|
|
73581
|
-
if (
|
|
74077
|
+
if (existsSync27(path2)) {
|
|
73582
74078
|
this.idle.add(path2);
|
|
73583
74079
|
} else {
|
|
73584
74080
|
worktreePoolLog.log(`Rehydrate skipped (not on disk): ${path2}`);
|
|
@@ -73634,7 +74130,7 @@ var init_worktree_pool = __esm({
|
|
|
73634
74130
|
throw err;
|
|
73635
74131
|
}
|
|
73636
74132
|
const conflictingPath = match[1];
|
|
73637
|
-
if (!
|
|
74133
|
+
if (!existsSync27(conflictingPath)) {
|
|
73638
74134
|
await execAsync3("git worktree prune", { cwd: worktreePath });
|
|
73639
74135
|
await execAsync3(checkoutCmd, { cwd: worktreePath });
|
|
73640
74136
|
return branchName;
|
|
@@ -73711,8 +74207,8 @@ var init_token_cap_detector = __esm({
|
|
|
73711
74207
|
// ../engine/src/step-session-executor.ts
|
|
73712
74208
|
import { exec as exec5 } from "node:child_process";
|
|
73713
74209
|
import { promisify as promisify6 } from "node:util";
|
|
73714
|
-
import { existsSync as
|
|
73715
|
-
import { join as
|
|
74210
|
+
import { existsSync as existsSync28 } from "node:fs";
|
|
74211
|
+
import { join as join35 } from "node:path";
|
|
73716
74212
|
function parseStepFileScopes(prompt) {
|
|
73717
74213
|
const result = /* @__PURE__ */ new Map();
|
|
73718
74214
|
if (!prompt) return result;
|
|
@@ -73959,7 +74455,7 @@ function buildReducedStepPrompt(taskDetail, stepIndex) {
|
|
|
73959
74455
|
return parts.join("\n").replace(/\n{3,}/g, "\n\n");
|
|
73960
74456
|
}
|
|
73961
74457
|
function sleep3(ms) {
|
|
73962
|
-
return new Promise((
|
|
74458
|
+
return new Promise((resolve24) => setTimeout(resolve24, ms));
|
|
73963
74459
|
}
|
|
73964
74460
|
var execAsync4, stepExecLog, MAX_STEP_RETRIES, RETRY_DELAYS_MS, NOOP_TASK_STORE, StepSessionExecutor;
|
|
73965
74461
|
var init_step_session_executor = __esm({
|
|
@@ -74086,7 +74582,7 @@ var init_step_session_executor = __esm({
|
|
|
74086
74582
|
}
|
|
74087
74583
|
for (const [stepIdx, worktreePath] of this.parallelWorktrees) {
|
|
74088
74584
|
try {
|
|
74089
|
-
if (
|
|
74585
|
+
if (existsSync28(worktreePath)) {
|
|
74090
74586
|
await execAsync4(`git worktree remove "${worktreePath}" --force`, {
|
|
74091
74587
|
cwd: this.options.rootDir
|
|
74092
74588
|
});
|
|
@@ -74442,7 +74938,7 @@ Follow instructions precisely and avoid unrelated changes.`,
|
|
|
74442
74938
|
for (const [stepIdx, worktreePath] of worktreePaths) {
|
|
74443
74939
|
if (worktreePath !== this.options.worktreePath) {
|
|
74444
74940
|
try {
|
|
74445
|
-
if (
|
|
74941
|
+
if (existsSync28(worktreePath)) {
|
|
74446
74942
|
await execAsync4(`git worktree remove "${worktreePath}" --force`, {
|
|
74447
74943
|
cwd: this.options.rootDir
|
|
74448
74944
|
});
|
|
@@ -74472,7 +74968,7 @@ Follow instructions precisely and avoid unrelated changes.`,
|
|
|
74472
74968
|
async createStepWorktree(stepIndex) {
|
|
74473
74969
|
const { rootDir } = this.options;
|
|
74474
74970
|
const name = generateWorktreeName(rootDir);
|
|
74475
|
-
const worktreePath =
|
|
74971
|
+
const worktreePath = join35(rootDir, ".worktrees", name);
|
|
74476
74972
|
const branchName = `fusion/step-${stepIndex}-${name}`;
|
|
74477
74973
|
stepExecLog.log(`Creating worktree for step ${stepIndex}: ${worktreePath} (branch: ${branchName})`);
|
|
74478
74974
|
try {
|
|
@@ -74546,8 +75042,8 @@ Follow instructions precisely and avoid unrelated changes.`,
|
|
|
74546
75042
|
});
|
|
74547
75043
|
|
|
74548
75044
|
// ../engine/src/spec-staleness.ts
|
|
74549
|
-
import { stat as
|
|
74550
|
-
import { join as
|
|
75045
|
+
import { stat as stat6 } from "node:fs/promises";
|
|
75046
|
+
import { join as join36 } from "node:path";
|
|
74551
75047
|
async function evaluateSpecStaleness(options) {
|
|
74552
75048
|
const { settings, promptPath, nowMs } = options;
|
|
74553
75049
|
if (settings.specStalenessEnabled !== true) {
|
|
@@ -74564,7 +75060,7 @@ async function evaluateSpecStaleness(options) {
|
|
|
74564
75060
|
const now = nowMs ?? Date.now();
|
|
74565
75061
|
let mtimeMs;
|
|
74566
75062
|
try {
|
|
74567
|
-
const fileStat = await
|
|
75063
|
+
const fileStat = await stat6(promptPath);
|
|
74568
75064
|
mtimeMs = fileStat.mtimeMs;
|
|
74569
75065
|
} catch {
|
|
74570
75066
|
return {
|
|
@@ -74587,7 +75083,7 @@ async function evaluateSpecStaleness(options) {
|
|
|
74587
75083
|
};
|
|
74588
75084
|
}
|
|
74589
75085
|
function getPromptPath(tasksDir, taskId) {
|
|
74590
|
-
return
|
|
75086
|
+
return join36(tasksDir, taskId, "PROMPT.md");
|
|
74591
75087
|
}
|
|
74592
75088
|
var DEFAULT_SPEC_STALENESS_MAX_AGE_MS;
|
|
74593
75089
|
var init_spec_staleness = __esm({
|
|
@@ -74618,8 +75114,8 @@ var init_task_completion = __esm({
|
|
|
74618
75114
|
|
|
74619
75115
|
// ../engine/src/run-verification-tool.ts
|
|
74620
75116
|
import { spawn as spawn4 } from "node:child_process";
|
|
74621
|
-
import { existsSync as
|
|
74622
|
-
import { isAbsolute as
|
|
75117
|
+
import { existsSync as existsSync29 } from "node:fs";
|
|
75118
|
+
import { isAbsolute as isAbsolute11, join as join37 } from "node:path";
|
|
74623
75119
|
import { Type as Type4 } from "@mariozechner/pi-ai";
|
|
74624
75120
|
function createBuffer() {
|
|
74625
75121
|
return { headChunks: [], headBytes: 0, tailChunks: [], tailBytes: 0, totalBytes: 0 };
|
|
@@ -74658,11 +75154,17 @@ async function runVerificationCommand3(opts) {
|
|
|
74658
75154
|
const warnings = [];
|
|
74659
75155
|
const stdoutBuf = createBuffer();
|
|
74660
75156
|
const stderrBuf = createBuffer();
|
|
74661
|
-
return new Promise((
|
|
75157
|
+
return new Promise((resolve24) => {
|
|
74662
75158
|
const child = spawn4(command, {
|
|
74663
75159
|
cwd,
|
|
74664
75160
|
stdio: ["ignore", "pipe", "pipe"],
|
|
74665
|
-
env: {
|
|
75161
|
+
env: {
|
|
75162
|
+
...process.env,
|
|
75163
|
+
// Corepack otherwise prompts interactively before fetching a pinned
|
|
75164
|
+
// packageManager version, which hangs the non-TTY child until the
|
|
75165
|
+
// hard timeout. Disable the prompt so it proceeds (or errors fast).
|
|
75166
|
+
COREPACK_ENABLE_DOWNLOAD_PROMPT: "0"
|
|
75167
|
+
},
|
|
74666
75168
|
shell: true
|
|
74667
75169
|
});
|
|
74668
75170
|
let timedOut = false;
|
|
@@ -74737,7 +75239,7 @@ async function runVerificationCommand3(opts) {
|
|
|
74737
75239
|
`[fn_run_verification] command failed (exit=${exitCode}, signal=${signal ?? "none"}): ${command}`
|
|
74738
75240
|
);
|
|
74739
75241
|
}
|
|
74740
|
-
|
|
75242
|
+
resolve24({
|
|
74741
75243
|
success,
|
|
74742
75244
|
exitCode,
|
|
74743
75245
|
durationMs,
|
|
@@ -74757,7 +75259,7 @@ async function runVerificationCommand3(opts) {
|
|
|
74757
75259
|
clearTimeout(hardTimer);
|
|
74758
75260
|
const durationMs = Date.now() - startMs;
|
|
74759
75261
|
warnings.push(`Spawn error: ${err.message}`);
|
|
74760
|
-
|
|
75262
|
+
resolve24({
|
|
74761
75263
|
success: false,
|
|
74762
75264
|
exitCode: null,
|
|
74763
75265
|
durationMs,
|
|
@@ -74789,10 +75291,10 @@ function createRunVerificationTool(opts) {
|
|
|
74789
75291
|
log18.warn(`[fn_run_verification] ${taskId}: ${msg}`);
|
|
74790
75292
|
}
|
|
74791
75293
|
let resolvedCwd;
|
|
74792
|
-
if (params.cwd &&
|
|
75294
|
+
if (params.cwd && isAbsolute11(params.cwd)) {
|
|
74793
75295
|
resolvedCwd = params.cwd;
|
|
74794
75296
|
} else if (params.cwd) {
|
|
74795
|
-
resolvedCwd =
|
|
75297
|
+
resolvedCwd = join37(worktreePath, params.cwd);
|
|
74796
75298
|
} else {
|
|
74797
75299
|
resolvedCwd = worktreePath;
|
|
74798
75300
|
}
|
|
@@ -74807,8 +75309,8 @@ function createRunVerificationTool(opts) {
|
|
|
74807
75309
|
}
|
|
74808
75310
|
let effectiveCommand = command;
|
|
74809
75311
|
if (command.trimStart().startsWith("pnpm --filter")) {
|
|
74810
|
-
const modulesYaml =
|
|
74811
|
-
if (!
|
|
75312
|
+
const modulesYaml = join37(rootDir, "node_modules", ".modules.yaml");
|
|
75313
|
+
if (!existsSync29(modulesYaml)) {
|
|
74812
75314
|
const installCmd = "pnpm install --prefer-offline";
|
|
74813
75315
|
const msg = `node_modules/.modules.yaml not found in workspace root \u2014 auto-prepending \`${installCmd}\` before running the command.`;
|
|
74814
75316
|
warnings.push(msg);
|
|
@@ -74919,9 +75421,9 @@ var init_run_verification_tool = __esm({
|
|
|
74919
75421
|
// ../engine/src/executor.ts
|
|
74920
75422
|
import { exec as exec6 } from "node:child_process";
|
|
74921
75423
|
import { promisify as promisify7 } from "node:util";
|
|
74922
|
-
import { isAbsolute as
|
|
74923
|
-
import { existsSync as
|
|
74924
|
-
import { readFile as
|
|
75424
|
+
import { isAbsolute as isAbsolute12, join as join38, relative as relative9, resolve as resolvePath } from "node:path";
|
|
75425
|
+
import { existsSync as existsSync30 } from "node:fs";
|
|
75426
|
+
import { readFile as readFile17, writeFile as writeFile13 } from "node:fs/promises";
|
|
74925
75427
|
import { Type as Type5 } from "@mariozechner/pi-ai";
|
|
74926
75428
|
import { ModelRegistry as ModelRegistry2, SessionManager as SessionManager2 } from "@mariozechner/pi-coding-agent";
|
|
74927
75429
|
function truncateWorkflowScriptOutput2(output) {
|
|
@@ -74983,7 +75485,7 @@ function getExecutorSystemPrompt(settings) {
|
|
|
74983
75485
|
].filter((section) => section.trim());
|
|
74984
75486
|
return sections.join("\n\n");
|
|
74985
75487
|
}
|
|
74986
|
-
function
|
|
75488
|
+
function formatTimestamp3(iso) {
|
|
74987
75489
|
const date = new Date(iso);
|
|
74988
75490
|
const now = /* @__PURE__ */ new Date();
|
|
74989
75491
|
const diffMs = now.getTime() - date.getTime();
|
|
@@ -75078,7 +75580,7 @@ git log --oneline
|
|
|
75078
75580
|
""
|
|
75079
75581
|
];
|
|
75080
75582
|
for (const comment of recentComments) {
|
|
75081
|
-
const timestamp =
|
|
75583
|
+
const timestamp = formatTimestamp3(comment.createdAt);
|
|
75082
75584
|
lines.push(`**${comment.author}** \u2014 ${timestamp}`);
|
|
75083
75585
|
lines.push(`> ${comment.text}`);
|
|
75084
75586
|
lines.push("");
|
|
@@ -75142,7 +75644,7 @@ If lint is configured and failing, fix that too before completion.
|
|
|
75142
75644
|
**CRITICAL: Resolve ALL test failures (and any lint/typecheck failures) before completing the task, even if they appear unrelated or pre-existing.** Unrelated failures left unfixed accumulate technical debt and block future integrations. Investigate and fix or suppress them \u2014 do not defer them to a separate task.`;
|
|
75143
75645
|
}
|
|
75144
75646
|
function formatCommentForInjection(comment) {
|
|
75145
|
-
const timestamp =
|
|
75647
|
+
const timestamp = formatTimestamp3(comment.createdAt);
|
|
75146
75648
|
return `\u{1F4E3} **New feedback** \u2014 ${timestamp} (${comment.author}):
|
|
75147
75649
|
|
|
75148
75650
|
${comment.text}
|
|
@@ -76545,7 +77047,7 @@ The tool prevents your session from being killed by the inactivity watchdog duri
|
|
|
76545
77047
|
);
|
|
76546
77048
|
return false;
|
|
76547
77049
|
}
|
|
76548
|
-
if (task.worktree &&
|
|
77050
|
+
if (task.worktree && existsSync30(task.worktree)) {
|
|
76549
77051
|
const modifiedFiles = await this.captureModifiedFiles(task.worktree, task.baseCommitSha);
|
|
76550
77052
|
if (modifiedFiles.length > 0) {
|
|
76551
77053
|
await this.store.updateTask(task.id, { modifiedFiles });
|
|
@@ -76802,7 +77304,7 @@ The tool prevents your session from being killed by the inactivity watchdog duri
|
|
|
76802
77304
|
const activeMergeStatuses = /* @__PURE__ */ new Set(["merging", "merging-pr", "merging-fix"]);
|
|
76803
77305
|
const isActiveTask = activeColumns.has(task.column) || activeMergeStatuses.has(task.status ?? "");
|
|
76804
77306
|
if (!isActiveTask) {
|
|
76805
|
-
const tasksDir =
|
|
77307
|
+
const tasksDir = join38(this.store.getFusionDir(), "tasks");
|
|
76806
77308
|
const promptPath = getPromptPath(tasksDir, task.id);
|
|
76807
77309
|
const staleness = await evaluateSpecStaleness({ settings, promptPath });
|
|
76808
77310
|
if (staleness.isStale) {
|
|
@@ -76849,7 +77351,7 @@ The tool prevents your session from being killed by the inactivity watchdog duri
|
|
|
76849
77351
|
worktreeName = generateWorktreeName(this.rootDir);
|
|
76850
77352
|
break;
|
|
76851
77353
|
}
|
|
76852
|
-
worktreePath =
|
|
77354
|
+
worktreePath = join38(this.rootDir, ".worktrees", worktreeName);
|
|
76853
77355
|
}
|
|
76854
77356
|
let stuckRequeue = null;
|
|
76855
77357
|
let taskDone = false;
|
|
@@ -76876,7 +77378,7 @@ The tool prevents your session from being killed by the inactivity watchdog duri
|
|
|
76876
77378
|
);
|
|
76877
77379
|
}
|
|
76878
77380
|
const branchName = task.branch || `fusion/${task.id.toLowerCase()}`;
|
|
76879
|
-
let isResume =
|
|
77381
|
+
let isResume = existsSync30(worktreePath);
|
|
76880
77382
|
let acquiredFromPool = false;
|
|
76881
77383
|
const baseBranch = task.executionStartBranch || null;
|
|
76882
77384
|
if (task.worktree && isResume && !await isUsableTaskWorktree(this.rootDir, worktreePath)) {
|
|
@@ -76889,8 +77391,8 @@ The tool prevents your session from being killed by the inactivity watchdog duri
|
|
|
76889
77391
|
this.currentRunContext
|
|
76890
77392
|
);
|
|
76891
77393
|
await this.store.updateTask(task.id, { worktree: null, branch: null });
|
|
76892
|
-
worktreePath =
|
|
76893
|
-
isResume =
|
|
77394
|
+
worktreePath = join38(this.rootDir, ".worktrees", generateWorktreeName(this.rootDir));
|
|
77395
|
+
isResume = existsSync30(worktreePath);
|
|
76894
77396
|
}
|
|
76895
77397
|
if (!isResume) {
|
|
76896
77398
|
if (this.options.pool && settings.recycleWorktrees) {
|
|
@@ -77289,7 +77791,7 @@ ${summary}`,
|
|
|
77289
77791
|
executorLog.warn(`\u26A1 ${task.id} transient error \u2014 retry ${attempt}/${MAX_RECOVERY_RETRIES} in ${delay3}: ${errorMessage}`);
|
|
77290
77792
|
await this.store.logEntry(task.id, `Transient error (retry ${attempt}/${MAX_RECOVERY_RETRIES} in ${delay3}): ${errorMessage}`, void 0, this.currentRunContext);
|
|
77291
77793
|
}
|
|
77292
|
-
if (worktreePath &&
|
|
77794
|
+
if (worktreePath && existsSync30(worktreePath)) {
|
|
77293
77795
|
try {
|
|
77294
77796
|
await execAsync5(`git worktree remove "${worktreePath}" --force`, { cwd: this.rootDir });
|
|
77295
77797
|
await audit.git({ type: "worktree:remove", target: worktreePath });
|
|
@@ -77357,7 +77859,7 @@ ${summary}`,
|
|
|
77357
77859
|
if (!preserveProgress) {
|
|
77358
77860
|
await this.resetStepsIfWorkLost(latestTask);
|
|
77359
77861
|
}
|
|
77360
|
-
if (worktreePath &&
|
|
77862
|
+
if (worktreePath && existsSync30(worktreePath)) {
|
|
77361
77863
|
try {
|
|
77362
77864
|
await execAsync5(`git worktree remove "${worktreePath}" --force`, { cwd: this.rootDir });
|
|
77363
77865
|
} catch (wtErr) {
|
|
@@ -77478,7 +77980,7 @@ ${summary}`,
|
|
|
77478
77980
|
const executorFallbackProvider = settings.fallbackProvider;
|
|
77479
77981
|
const executorFallbackModelId = settings.fallbackModelId;
|
|
77480
77982
|
const executorThinkingLevel = detail.thinkingLevel ?? settings.defaultThinkingLevel;
|
|
77481
|
-
const isResuming = !!task.sessionFile &&
|
|
77983
|
+
const isResuming = !!task.sessionFile && existsSync30(task.sessionFile);
|
|
77482
77984
|
const sessionManager = isResuming ? SessionManager2.open(task.sessionFile) : SessionManager2.create(worktreePath);
|
|
77483
77985
|
executorLog.log(`${task.id}: creating agent session (provider=${executorProvider ?? "default"}, model=${executorModelId ?? "default"}, resuming=${isResuming})`);
|
|
77484
77986
|
const executorInstructions = await this.resolveInstructionsForRole("executor");
|
|
@@ -77993,7 +78495,7 @@ ${executorPluginContributions}` : executorSystemPrompt;
|
|
|
77993
78495
|
this.options.onComplete?.(task);
|
|
77994
78496
|
} else {
|
|
77995
78497
|
executorLog.log(`${task.id} paused \u2014 moving to todo`);
|
|
77996
|
-
if (worktreePath &&
|
|
78498
|
+
if (worktreePath && existsSync30(worktreePath)) {
|
|
77997
78499
|
try {
|
|
77998
78500
|
await execAsync5(`git worktree remove "${worktreePath}" --force`, { cwd: this.rootDir });
|
|
77999
78501
|
executorLog.log(`Removed old worktree for paused task: ${worktreePath}`);
|
|
@@ -78089,7 +78591,7 @@ ${executorPluginContributions}` : executorSystemPrompt;
|
|
|
78089
78591
|
executorLog.warn(`\u26A1 ${task.id} transient error \u2014 retry ${attempt}/${MAX_RECOVERY_RETRIES} in ${delay3}: ${errorMessage}`);
|
|
78090
78592
|
await this.store.logEntry(task.id, `Transient error (retry ${attempt}/${MAX_RECOVERY_RETRIES} in ${delay3}): ${errorMessage}`, void 0, this.currentRunContext);
|
|
78091
78593
|
}
|
|
78092
|
-
if (worktreePath &&
|
|
78594
|
+
if (worktreePath && existsSync30(worktreePath)) {
|
|
78093
78595
|
try {
|
|
78094
78596
|
await execAsync5(`git worktree remove "${worktreePath}" --force`, { cwd: this.rootDir });
|
|
78095
78597
|
executorLog.log(`Removed old worktree for transient retry: ${worktreePath}`);
|
|
@@ -78161,7 +78663,7 @@ ${executorPluginContributions}` : executorSystemPrompt;
|
|
|
78161
78663
|
if (!preserveProgress) {
|
|
78162
78664
|
await this.resetStepsIfWorkLost(latestTask);
|
|
78163
78665
|
}
|
|
78164
|
-
if (worktreePath &&
|
|
78666
|
+
if (worktreePath && existsSync30(worktreePath)) {
|
|
78165
78667
|
try {
|
|
78166
78668
|
await execAsync5(`git worktree remove "${worktreePath}" --force`, { cwd: this.rootDir });
|
|
78167
78669
|
executorLog.log(`Removed old worktree for stuck-killed retry: ${worktreePath}`);
|
|
@@ -78731,10 +79233,10 @@ Take a different approach. Do NOT repeat the rejected strategy. Re-read the step
|
|
|
78731
79233
|
* The section is replaced entirely to avoid accumulation of old feedback.
|
|
78732
79234
|
*/
|
|
78733
79235
|
async injectWorkflowRevisionInstructions(task, feedback) {
|
|
78734
|
-
const promptPath =
|
|
79236
|
+
const promptPath = join38(this.store.getFusionDir(), "tasks", task.id, "PROMPT.md");
|
|
78735
79237
|
let content;
|
|
78736
79238
|
try {
|
|
78737
|
-
content = await
|
|
79239
|
+
content = await readFile17(promptPath, "utf-8");
|
|
78738
79240
|
} catch {
|
|
78739
79241
|
executorLog.warn(`${task.id}: PROMPT.md not found at ${promptPath}, skipping revision injection`);
|
|
78740
79242
|
return;
|
|
@@ -78771,7 +79273,7 @@ ${feedback}
|
|
|
78771
79273
|
}
|
|
78772
79274
|
}
|
|
78773
79275
|
try {
|
|
78774
|
-
await
|
|
79276
|
+
await writeFile13(promptPath, newContent);
|
|
78775
79277
|
executorLog.log(`${task.id}: injected workflow revision instructions into PROMPT.md`);
|
|
78776
79278
|
} catch (err) {
|
|
78777
79279
|
const errorMessage = err instanceof Error ? err.message : String(err);
|
|
@@ -79067,10 +79569,10 @@ Please fix the issues so the verification can pass on the next attempt.`,
|
|
|
79067
79569
|
* The section is replaced entirely to avoid accumulation of old feedback.
|
|
79068
79570
|
*/
|
|
79069
79571
|
async injectWorkflowStepFailureInstructions(task, failureFeedback, stepName, retryCount) {
|
|
79070
|
-
const promptPath =
|
|
79572
|
+
const promptPath = join38(this.store.getFusionDir(), "tasks", task.id, "PROMPT.md");
|
|
79071
79573
|
let content;
|
|
79072
79574
|
try {
|
|
79073
|
-
content = await
|
|
79575
|
+
content = await readFile17(promptPath, "utf-8");
|
|
79074
79576
|
} catch {
|
|
79075
79577
|
executorLog.warn(`${task.id}: PROMPT.md not found at ${promptPath}, skipping workflow failure injection`);
|
|
79076
79578
|
return;
|
|
@@ -79120,7 +79622,7 @@ ${failureFeedback}
|
|
|
79120
79622
|
}
|
|
79121
79623
|
}
|
|
79122
79624
|
try {
|
|
79123
|
-
await
|
|
79625
|
+
await writeFile13(promptPath, newContent);
|
|
79124
79626
|
executorLog.log(`${task.id}: injected workflow step failure instructions into PROMPT.md (retry ${retryCount}/${MAX_WORKFLOW_STEP_RETRIES})`);
|
|
79125
79627
|
} catch (err) {
|
|
79126
79628
|
const errorMessage = err instanceof Error ? err.message : String(err);
|
|
@@ -79677,7 +80179,7 @@ Review the work done in this worktree and evaluate it against the criteria in yo
|
|
|
79677
80179
|
);
|
|
79678
80180
|
}
|
|
79679
80181
|
const delay3 = this.WORKTREE_RETRY_DELAYS[attempt] || 1e3;
|
|
79680
|
-
await new Promise((
|
|
80182
|
+
await new Promise((resolve24) => setTimeout(resolve24, delay3));
|
|
79681
80183
|
}
|
|
79682
80184
|
}
|
|
79683
80185
|
throw new Error("Unexpected exit from worktree creation retry loop");
|
|
@@ -79899,7 +80401,7 @@ Review the work done in this worktree and evaluate it against the criteria in yo
|
|
|
79899
80401
|
* rather than fail the task permanently.
|
|
79900
80402
|
*/
|
|
79901
80403
|
async resolveWorktreeStartPoint(startPoint, taskId) {
|
|
79902
|
-
const command =
|
|
80404
|
+
const command = isAbsolute12(startPoint) && existsSync30(startPoint) ? `git -C "${startPoint}" rev-parse --verify HEAD^{commit}` : `git rev-parse --verify "${startPoint}^{commit}"`;
|
|
79903
80405
|
try {
|
|
79904
80406
|
const { stdout } = await execAsync5(command, { cwd: this.rootDir });
|
|
79905
80407
|
return stdout.trim() || startPoint;
|
|
@@ -79919,7 +80421,7 @@ Review the work done in this worktree and evaluate it against the criteria in yo
|
|
|
79919
80421
|
*/
|
|
79920
80422
|
async tryCreateWorktree(branch, path2, taskId, startPoint, attemptNumber = 0, recoveryDepth = 0) {
|
|
79921
80423
|
await this.assertWorktreePathNotNested(path2, taskId);
|
|
79922
|
-
if (
|
|
80424
|
+
if (existsSync30(path2)) {
|
|
79923
80425
|
const isRegistered = await this.isRegisteredWorktree(path2);
|
|
79924
80426
|
if (!isRegistered) {
|
|
79925
80427
|
await this.store.logEntry(
|
|
@@ -80070,7 +80572,7 @@ Review the work done in this worktree and evaluate it against the criteria in yo
|
|
|
80070
80572
|
);
|
|
80071
80573
|
if (shouldGenerateNewName) {
|
|
80072
80574
|
const conflictStartPoint = branch;
|
|
80073
|
-
const newPath =
|
|
80575
|
+
const newPath = join38(this.rootDir, ".worktrees", generateWorktreeName(this.rootDir));
|
|
80074
80576
|
for (let suffix = 2; suffix <= 6; suffix++) {
|
|
80075
80577
|
const suffixedBranch = `${branch}-${suffix}`;
|
|
80076
80578
|
try {
|
|
@@ -80117,8 +80619,8 @@ Review the work done in this worktree and evaluate it against the criteria in yo
|
|
|
80117
80619
|
for (const wt of registered) {
|
|
80118
80620
|
if (wt === rootResolved) continue;
|
|
80119
80621
|
if (wt === target) continue;
|
|
80120
|
-
const rel =
|
|
80121
|
-
if (rel && !rel.startsWith("..") && !
|
|
80622
|
+
const rel = relative9(wt, target);
|
|
80623
|
+
if (rel && !rel.startsWith("..") && !isAbsolute12(rel)) {
|
|
80122
80624
|
await this.store.logEntry(
|
|
80123
80625
|
taskId,
|
|
80124
80626
|
`Refusing to create nested worktree`,
|
|
@@ -80694,7 +81196,7 @@ Review the work done in this worktree and evaluate it against the criteria in yo
|
|
|
80694
81196
|
metadata: { type: "spawned", parentTaskId: taskId }
|
|
80695
81197
|
});
|
|
80696
81198
|
const childWorktreeName = generateWorktreeName(this.rootDir);
|
|
80697
|
-
const childWorktreePath =
|
|
81199
|
+
const childWorktreePath = join38(this.rootDir, ".worktrees", childWorktreeName);
|
|
80698
81200
|
const childBranch = `fusion/spawn-${agent.id}`;
|
|
80699
81201
|
await this.createWorktree(childBranch, childWorktreePath, taskId, worktreePath);
|
|
80700
81202
|
await this.options.agentStore.updateAgentState(agent.id, "active");
|
|
@@ -81090,9 +81592,9 @@ var init_node_routing_policy = __esm({
|
|
|
81090
81592
|
});
|
|
81091
81593
|
|
|
81092
81594
|
// ../engine/src/scheduler.ts
|
|
81093
|
-
import { existsSync as
|
|
81094
|
-
import { readFile as
|
|
81095
|
-
import { join as
|
|
81595
|
+
import { existsSync as existsSync31 } from "node:fs";
|
|
81596
|
+
import { readFile as readFile18 } from "node:fs/promises";
|
|
81597
|
+
import { join as join39 } from "node:path";
|
|
81096
81598
|
function pathsOverlap2(a, b) {
|
|
81097
81599
|
for (const pa of a) {
|
|
81098
81600
|
const prefixA = pa.endsWith("/*") ? pa.slice(0, -1) : null;
|
|
@@ -81264,16 +81766,16 @@ var init_scheduler = __esm({
|
|
|
81264
81766
|
* @returns Object with `valid: true` if checks pass, or `valid: false` with a `reason` string if they fail
|
|
81265
81767
|
*/
|
|
81266
81768
|
async validateTaskFilesystem(id) {
|
|
81267
|
-
const taskDir =
|
|
81268
|
-
if (!
|
|
81769
|
+
const taskDir = join39(this.store.getTasksDir(), id);
|
|
81770
|
+
if (!existsSync31(taskDir)) {
|
|
81269
81771
|
return { valid: false, reason: "missing directory" };
|
|
81270
81772
|
}
|
|
81271
|
-
const promptPath =
|
|
81272
|
-
if (!
|
|
81773
|
+
const promptPath = join39(taskDir, "PROMPT.md");
|
|
81774
|
+
if (!existsSync31(promptPath)) {
|
|
81273
81775
|
return { valid: false, reason: "missing or empty PROMPT.md" };
|
|
81274
81776
|
}
|
|
81275
81777
|
try {
|
|
81276
|
-
const content = await
|
|
81778
|
+
const content = await readFile18(promptPath, "utf-8");
|
|
81277
81779
|
if (!content || content.trim().length === 0) {
|
|
81278
81780
|
return { valid: false, reason: "missing or empty PROMPT.md" };
|
|
81279
81781
|
}
|
|
@@ -83470,8 +83972,8 @@ var agent_reflection_exports = {};
|
|
|
83470
83972
|
__export(agent_reflection_exports, {
|
|
83471
83973
|
AgentReflectionService: () => AgentReflectionService
|
|
83472
83974
|
});
|
|
83473
|
-
import { readFile as
|
|
83474
|
-
import { isAbsolute as
|
|
83975
|
+
import { readFile as readFile19 } from "node:fs/promises";
|
|
83976
|
+
import { isAbsolute as isAbsolute13, resolve as resolve19 } from "node:path";
|
|
83475
83977
|
var reflectionLog, REFLECTION_SYSTEM_PROMPT, DEFAULT_OUTCOME_LIMIT, AgentReflectionService;
|
|
83476
83978
|
var init_agent_reflection = __esm({
|
|
83477
83979
|
"../engine/src/agent-reflection.ts"() {
|
|
@@ -83782,9 +84284,9 @@ Rules:
|
|
|
83782
84284
|
pieces.push(agent.instructionsText.trim());
|
|
83783
84285
|
}
|
|
83784
84286
|
if (agent.instructionsPath?.trim()) {
|
|
83785
|
-
const resolvedPath =
|
|
84287
|
+
const resolvedPath = isAbsolute13(agent.instructionsPath) ? agent.instructionsPath : resolve19(this.rootDir, agent.instructionsPath);
|
|
83786
84288
|
try {
|
|
83787
|
-
const content = await
|
|
84289
|
+
const content = await readFile19(resolvedPath, "utf-8");
|
|
83788
84290
|
if (content.trim()) {
|
|
83789
84291
|
pieces.push(content.trim());
|
|
83790
84292
|
}
|
|
@@ -87273,7 +87775,7 @@ var init_evaluator = __esm({
|
|
|
87273
87775
|
// ../engine/src/cron-runner.ts
|
|
87274
87776
|
import { exec as exec7 } from "node:child_process";
|
|
87275
87777
|
function execCommand(command, options) {
|
|
87276
|
-
return new Promise((
|
|
87778
|
+
return new Promise((resolve24, reject) => {
|
|
87277
87779
|
exec7(command, options, (error, stdout, stderr) => {
|
|
87278
87780
|
const stdoutText = typeof stdout === "string" ? stdout : String(stdout ?? "");
|
|
87279
87781
|
const stderrText = typeof stderr === "string" ? stderr : String(stderr ?? "");
|
|
@@ -87284,7 +87786,7 @@ function execCommand(command, options) {
|
|
|
87284
87786
|
reject(errWithOutput);
|
|
87285
87787
|
return;
|
|
87286
87788
|
}
|
|
87287
|
-
|
|
87789
|
+
resolve24({ stdout: stdoutText, stderr: stderrText });
|
|
87288
87790
|
});
|
|
87289
87791
|
});
|
|
87290
87792
|
}
|
|
@@ -87318,6 +87820,36 @@ function isInProcessBackupCommand(command) {
|
|
|
87318
87820
|
}
|
|
87319
87821
|
return true;
|
|
87320
87822
|
}
|
|
87823
|
+
function isInProcessMemoryBackupCommand(command) {
|
|
87824
|
+
if (!command) return false;
|
|
87825
|
+
const trimmed = command.trim();
|
|
87826
|
+
if (!trimmed) return false;
|
|
87827
|
+
if (SHELL_METACHARACTERS_REGEX.test(trimmed)) return false;
|
|
87828
|
+
const tokens = trimmed.split(/\s+/).map((tok) => tok.toLowerCase());
|
|
87829
|
+
let cursor = 0;
|
|
87830
|
+
if (tokens[cursor] === "npx") {
|
|
87831
|
+
cursor += 1;
|
|
87832
|
+
while (cursor < tokens.length) {
|
|
87833
|
+
const tok = tokens[cursor];
|
|
87834
|
+
if (tok === void 0 || !tok.startsWith("-")) break;
|
|
87835
|
+
const takesValue = (tok === "-p" || tok === "--package") && cursor + 1 < tokens.length && tokens[cursor + 1] !== void 0 && !tokens[cursor + 1].startsWith("-");
|
|
87836
|
+
cursor += takesValue ? 2 : 1;
|
|
87837
|
+
}
|
|
87838
|
+
}
|
|
87839
|
+
const binary = tokens[cursor];
|
|
87840
|
+
if (!binary || !FUSION_BINARY_TOKENS.has(binary)) return false;
|
|
87841
|
+
cursor += 1;
|
|
87842
|
+
if (tokens[cursor] !== "memory-backup") return false;
|
|
87843
|
+
cursor += 1;
|
|
87844
|
+
if (tokens[cursor] !== "--create") return false;
|
|
87845
|
+
cursor += 1;
|
|
87846
|
+
for (; cursor < tokens.length; cursor += 1) {
|
|
87847
|
+
const tok = tokens[cursor];
|
|
87848
|
+
if (!tok) continue;
|
|
87849
|
+
if (!tok.startsWith("-")) return false;
|
|
87850
|
+
}
|
|
87851
|
+
return true;
|
|
87852
|
+
}
|
|
87321
87853
|
function isInProcessScheduledEvalCommand(command) {
|
|
87322
87854
|
if (!command) return false;
|
|
87323
87855
|
const trimmed = command.trim();
|
|
@@ -87525,6 +88057,9 @@ var init_cron_runner = __esm({
|
|
|
87525
88057
|
if (isInProcessBackupCommand(schedule.command)) {
|
|
87526
88058
|
return this.executeBackupInProcess(schedule, startedAt);
|
|
87527
88059
|
}
|
|
88060
|
+
if (isInProcessMemoryBackupCommand(schedule.command)) {
|
|
88061
|
+
return this.executeMemoryBackupInProcess(schedule, startedAt);
|
|
88062
|
+
}
|
|
87528
88063
|
if (isInProcessScheduledEvalCommand(schedule.command)) {
|
|
87529
88064
|
return this.executeScheduledEvalInProcess(schedule, startedAt);
|
|
87530
88065
|
}
|
|
@@ -87578,6 +88113,21 @@ var init_cron_runner = __esm({
|
|
|
87578
88113
|
completedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
87579
88114
|
};
|
|
87580
88115
|
}
|
|
88116
|
+
async executeMemoryBackupInProcess(schedule, startedAt) {
|
|
88117
|
+
const action = await this.runMemoryBackupActionInProcess();
|
|
88118
|
+
if (action.success) {
|
|
88119
|
+
log15.log(`\u2713 ${schedule.name} completed in-process`);
|
|
88120
|
+
} else {
|
|
88121
|
+
log15.warn(`\u2717 ${schedule.name} in-process memory backup ${action.error ? `threw: ${action.error}` : `reported failure: ${action.output}`}`);
|
|
88122
|
+
}
|
|
88123
|
+
return {
|
|
88124
|
+
success: action.success,
|
|
88125
|
+
output: action.output,
|
|
88126
|
+
error: action.error,
|
|
88127
|
+
startedAt,
|
|
88128
|
+
completedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
88129
|
+
};
|
|
88130
|
+
}
|
|
87581
88131
|
/**
|
|
87582
88132
|
* Shared in-process backup execution used by both the legacy-command path
|
|
87583
88133
|
* and the command-step path. Returns the success/output/error tuple in
|
|
@@ -87635,6 +88185,22 @@ var init_cron_runner = __esm({
|
|
|
87635
88185
|
return { success: false, output: "", error: message };
|
|
87636
88186
|
}
|
|
87637
88187
|
}
|
|
88188
|
+
async runMemoryBackupActionInProcess() {
|
|
88189
|
+
try {
|
|
88190
|
+
const { runMemoryBackupCommand: runMemoryBackupCommand2 } = await Promise.resolve().then(() => (init_src(), src_exports));
|
|
88191
|
+
const fusionDir = this.store.getFusionDir();
|
|
88192
|
+
const settings = await this.store.getSettings();
|
|
88193
|
+
const result = await runMemoryBackupCommand2(fusionDir, settings);
|
|
88194
|
+
return {
|
|
88195
|
+
success: result.success,
|
|
88196
|
+
output: truncateOutput2(result.output ?? "", ""),
|
|
88197
|
+
error: result.success ? void 0 : result.output
|
|
88198
|
+
};
|
|
88199
|
+
} catch (err) {
|
|
88200
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
88201
|
+
return { success: false, output: "", error: message };
|
|
88202
|
+
}
|
|
88203
|
+
}
|
|
87638
88204
|
/**
|
|
87639
88205
|
* Execute multiple steps sequentially.
|
|
87640
88206
|
* Aggregates per-step results into an overall AutomationRunResult.
|
|
@@ -87736,6 +88302,19 @@ var init_cron_runner = __esm({
|
|
|
87736
88302
|
completedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
87737
88303
|
};
|
|
87738
88304
|
}
|
|
88305
|
+
if (isInProcessMemoryBackupCommand(step.command)) {
|
|
88306
|
+
const action = await this.runMemoryBackupActionInProcess();
|
|
88307
|
+
return {
|
|
88308
|
+
stepId: step.id,
|
|
88309
|
+
stepName: step.name,
|
|
88310
|
+
stepIndex,
|
|
88311
|
+
success: action.success,
|
|
88312
|
+
output: action.output,
|
|
88313
|
+
error: action.error,
|
|
88314
|
+
startedAt,
|
|
88315
|
+
completedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
88316
|
+
};
|
|
88317
|
+
}
|
|
87739
88318
|
try {
|
|
87740
88319
|
const { stdout, stderr } = await execCommand(step.command, {
|
|
87741
88320
|
timeout: timeoutMs,
|
|
@@ -88109,6 +88688,30 @@ var init_routine_runner = __esm({
|
|
|
88109
88688
|
};
|
|
88110
88689
|
}
|
|
88111
88690
|
}
|
|
88691
|
+
if (isInProcessMemoryBackupCommand(command) && this.options.taskStore) {
|
|
88692
|
+
try {
|
|
88693
|
+
const { runMemoryBackupCommand: runMemoryBackupCommand2 } = await Promise.resolve().then(() => (init_src(), src_exports));
|
|
88694
|
+
const fusionDir = this.options.taskStore.getFusionDir();
|
|
88695
|
+
const settings = await this.options.taskStore.getSettings();
|
|
88696
|
+
const result = await runMemoryBackupCommand2(fusionDir, settings);
|
|
88697
|
+
return {
|
|
88698
|
+
success: result.success,
|
|
88699
|
+
output: truncateOutput3(result.output ?? "", ""),
|
|
88700
|
+
error: result.success ? void 0 : result.output,
|
|
88701
|
+
startedAt,
|
|
88702
|
+
completedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
88703
|
+
};
|
|
88704
|
+
} catch (err) {
|
|
88705
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
88706
|
+
return {
|
|
88707
|
+
success: false,
|
|
88708
|
+
output: "",
|
|
88709
|
+
error: message,
|
|
88710
|
+
startedAt,
|
|
88711
|
+
completedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
88712
|
+
};
|
|
88713
|
+
}
|
|
88714
|
+
}
|
|
88112
88715
|
try {
|
|
88113
88716
|
const { stdout, stderr } = await execAsync6(command, {
|
|
88114
88717
|
timeout: timeoutMs ?? DEFAULT_TIMEOUT_MS8,
|
|
@@ -88835,8 +89438,8 @@ var init_stuck_task_detector = __esm({
|
|
|
88835
89438
|
// ../engine/src/self-healing.ts
|
|
88836
89439
|
import { exec as exec9, execSync as execSync2 } from "node:child_process";
|
|
88837
89440
|
import { promisify as promisify9 } from "node:util";
|
|
88838
|
-
import { existsSync as
|
|
88839
|
-
import { isAbsolute as
|
|
89441
|
+
import { existsSync as existsSync32, readdirSync as readdirSync5, rmSync as rmSync3, statSync as statSync5 } from "node:fs";
|
|
89442
|
+
import { isAbsolute as isAbsolute14, join as join40, relative as relative10, resolve as resolve20 } from "node:path";
|
|
88840
89443
|
function commitOwnedByTask2(taskId, subject, body) {
|
|
88841
89444
|
return body.includes(`Fusion-Task-Id: ${taskId}`) || subject.includes(taskId);
|
|
88842
89445
|
}
|
|
@@ -88873,7 +89476,7 @@ function isNoTaskDoneFailure(task) {
|
|
|
88873
89476
|
function hasStepProgress(task) {
|
|
88874
89477
|
return task.steps.some((step) => step.status !== "pending");
|
|
88875
89478
|
}
|
|
88876
|
-
var log17, execAsync7, APPROVED_TRIAGE_RECOVERY_GRACE_MS, ORPHANED_EXECUTION_RECOVERY_GRACE_MS, ACTIVE_MERGE_STATUSES, NON_TERMINAL_STEP_STATUSES2, GHOST_REVIEW_PRESERVED_STATUSES, ORPHANED_WITH_WORKTREE_GRACE_MS, MAX_TASK_DONE_RETRIES, MAX_AUTO_MERGE_RETRIES, DEFAULT_STALE_MERGING_STATUS_MIN_AGE_MS, SelfHealingManager;
|
|
89479
|
+
var log17, execAsync7, APPROVED_TRIAGE_RECOVERY_GRACE_MS, ORPHANED_EXECUTION_RECOVERY_GRACE_MS, ACTIVE_MERGE_STATUSES, NON_TERMINAL_STEP_STATUSES2, GHOST_REVIEW_PRESERVED_STATUSES, ORPHANED_WITH_WORKTREE_GRACE_MS, MAX_TASK_DONE_RETRIES, MAX_AUTO_MERGE_RETRIES, DEADLOCK_RECOVERY_COOLDOWN_MS, DEFAULT_STALE_MERGING_STATUS_MIN_AGE_MS, SelfHealingManager;
|
|
88877
89480
|
var init_self_healing = __esm({
|
|
88878
89481
|
"../engine/src/self-healing.ts"() {
|
|
88879
89482
|
"use strict";
|
|
@@ -88897,6 +89500,7 @@ var init_self_healing = __esm({
|
|
|
88897
89500
|
ORPHANED_WITH_WORKTREE_GRACE_MS = 3e5;
|
|
88898
89501
|
MAX_TASK_DONE_RETRIES = 3;
|
|
88899
89502
|
MAX_AUTO_MERGE_RETRIES = 3;
|
|
89503
|
+
DEADLOCK_RECOVERY_COOLDOWN_MS = 15 * 6e4;
|
|
88900
89504
|
DEFAULT_STALE_MERGING_STATUS_MIN_AGE_MS = 5 * 6e4;
|
|
88901
89505
|
SelfHealingManager = class _SelfHealingManager {
|
|
88902
89506
|
constructor(store, options) {
|
|
@@ -88913,6 +89517,8 @@ var init_self_healing = __esm({
|
|
|
88913
89517
|
maintenanceRunning = false;
|
|
88914
89518
|
// ── Event listener cleanup ──────────────────────────────────────────
|
|
88915
89519
|
settingsListener = null;
|
|
89520
|
+
// ── Per-task deadlock recovery cooldown ─────────────────────────────
|
|
89521
|
+
deadlockRecoveryCooldown = /* @__PURE__ */ new Map();
|
|
88916
89522
|
// ── Lifecycle ───────────────────────────────────────────────────────
|
|
88917
89523
|
start() {
|
|
88918
89524
|
this.settingsListener = ({ settings, previous }) => {
|
|
@@ -88944,6 +89550,8 @@ var init_self_healing = __esm({
|
|
|
88944
89550
|
{ name: "failed-pre-merge-steps", fn: () => this.recoverReviewTasksWithFailedPreMergeSteps().then(() => void 0) },
|
|
88945
89551
|
{ name: "interrupted-merging", fn: () => this.recoverInterruptedMergingTasks().then(() => void 0) },
|
|
88946
89552
|
{ name: "done-merge-metadata", fn: () => this.recoverDoneTaskMergeMetadata().then(() => void 0) },
|
|
89553
|
+
{ name: "recover-already-merged-review", fn: () => this.recoverAlreadyMergedReviewTasks().then(() => void 0) },
|
|
89554
|
+
{ name: "recover-stuck-merge-deadlocks", fn: () => this.recoverStuckMergeDeadlocks().then(() => void 0) },
|
|
88947
89555
|
{ name: "misclassified-failures", fn: () => this.recoverMisclassifiedFailures().then(() => void 0) },
|
|
88948
89556
|
{ name: "partial-progress-no-task-done", fn: () => this.recoverPartialProgressNoTaskDoneFailures().then(() => void 0) },
|
|
88949
89557
|
{ name: "orphaned-executions", fn: () => this.recoverOrphanedExecutions().then(() => void 0) },
|
|
@@ -89351,7 +89959,7 @@ var init_self_healing = __esm({
|
|
|
89351
89959
|
return null;
|
|
89352
89960
|
}
|
|
89353
89961
|
async cleanupWorktreeOnly(task) {
|
|
89354
|
-
if (task.worktree &&
|
|
89962
|
+
if (task.worktree && existsSync32(task.worktree)) {
|
|
89355
89963
|
try {
|
|
89356
89964
|
await execAsync7(`git worktree remove ${shellQuote(task.worktree)} --force`, {
|
|
89357
89965
|
cwd: this.options.rootDir,
|
|
@@ -89366,7 +89974,7 @@ var init_self_healing = __esm({
|
|
|
89366
89974
|
}
|
|
89367
89975
|
}
|
|
89368
89976
|
async cleanupInterruptedMergeArtifacts(task) {
|
|
89369
|
-
if (task.worktree &&
|
|
89977
|
+
if (task.worktree && existsSync32(task.worktree)) {
|
|
89370
89978
|
try {
|
|
89371
89979
|
await execAsync7(`git worktree remove ${shellQuote(task.worktree)} --force`, {
|
|
89372
89980
|
cwd: this.options.rootDir,
|
|
@@ -89432,6 +90040,7 @@ var init_self_healing = __esm({
|
|
|
89432
90040
|
{ name: "recover-mergeable-review", fn: () => this.recoverMergeableReviewTasks() },
|
|
89433
90041
|
{ name: "recover-merged-review", fn: () => this.recoverMergedReviewTasks() },
|
|
89434
90042
|
{ name: "recover-already-merged-review", fn: () => this.recoverAlreadyMergedReviewTasks() },
|
|
90043
|
+
{ name: "recover-stuck-merge-deadlocks", fn: () => this.recoverStuckMergeDeadlocks() },
|
|
89435
90044
|
{ name: "recover-misclassified-failures", fn: () => this.recoverMisclassifiedFailures() },
|
|
89436
90045
|
{ name: "recover-no-progress-no-task-done", fn: () => this.recoverNoProgressNoTaskDoneFailures() },
|
|
89437
90046
|
{ name: "recover-partial-progress-no-task-done", fn: () => this.recoverPartialProgressNoTaskDoneFailures() },
|
|
@@ -90119,6 +90728,99 @@ var init_self_healing = __esm({
|
|
|
90119
90728
|
return 0;
|
|
90120
90729
|
}
|
|
90121
90730
|
}
|
|
90731
|
+
/**
|
|
90732
|
+
* Recover deadlocked retry-exhausted merge failures that are still blocking
|
|
90733
|
+
* dispatch via `blockedBy` or retained worktree ownership.
|
|
90734
|
+
*/
|
|
90735
|
+
async recoverStuckMergeDeadlocks() {
|
|
90736
|
+
try {
|
|
90737
|
+
const settings = await this.store.getSettings();
|
|
90738
|
+
if (settings.globalPause || settings.enginePaused) return 0;
|
|
90739
|
+
const now = Date.now();
|
|
90740
|
+
const inReview = await this.store.listTasks({ column: "in-review", slim: true });
|
|
90741
|
+
const triage = await this.store.listTasks({ column: "triage", slim: true });
|
|
90742
|
+
const todo = await this.store.listTasks({ column: "todo", slim: true });
|
|
90743
|
+
const inProgress = await this.store.listTasks({ column: "in-progress", slim: true });
|
|
90744
|
+
const dependentsByBlocker = /* @__PURE__ */ new Map();
|
|
90745
|
+
for (const task of [...triage, ...todo, ...inProgress]) {
|
|
90746
|
+
if (!task.blockedBy) continue;
|
|
90747
|
+
const dependents = dependentsByBlocker.get(task.blockedBy) ?? [];
|
|
90748
|
+
dependents.push(task);
|
|
90749
|
+
dependentsByBlocker.set(task.blockedBy, dependents);
|
|
90750
|
+
}
|
|
90751
|
+
const candidates = inReview.filter((task) => {
|
|
90752
|
+
const cooldownStart = this.deadlockRecoveryCooldown.get(task.id) ?? 0;
|
|
90753
|
+
const cooldownElapsed = now - cooldownStart;
|
|
90754
|
+
const hasBlockedDependents = (dependentsByBlocker.get(task.id) ?? []).some(
|
|
90755
|
+
(dep) => dep.column === "triage" || dep.column === "todo"
|
|
90756
|
+
);
|
|
90757
|
+
return task.column === "in-review" && !task.paused && task.status === "failed" && (task.mergeRetries ?? 0) >= MAX_AUTO_MERGE_RETRIES && task.mergeDetails?.mergeConfirmed !== true && (hasBlockedDependents || Boolean(task.worktree)) && cooldownElapsed >= DEADLOCK_RECOVERY_COOLDOWN_MS;
|
|
90758
|
+
});
|
|
90759
|
+
if (candidates.length === 0) return 0;
|
|
90760
|
+
let recovered = 0;
|
|
90761
|
+
for (const task of candidates) {
|
|
90762
|
+
const blockedDependents = dependentsByBlocker.get(task.id) ?? [];
|
|
90763
|
+
const blockedTaskIds = blockedDependents.map((dep) => dep.id);
|
|
90764
|
+
try {
|
|
90765
|
+
const landedCommit = await this.findLandedTaskCommit(task);
|
|
90766
|
+
if (landedCommit) {
|
|
90767
|
+
const mergeDetails = {
|
|
90768
|
+
commitSha: landedCommit.sha,
|
|
90769
|
+
filesChanged: landedCommit.filesChanged,
|
|
90770
|
+
insertions: landedCommit.insertions,
|
|
90771
|
+
deletions: landedCommit.deletions,
|
|
90772
|
+
mergeCommitMessage: landedCommit.subject,
|
|
90773
|
+
mergedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
90774
|
+
mergeConfirmed: true,
|
|
90775
|
+
prNumber: task.prInfo?.number
|
|
90776
|
+
};
|
|
90777
|
+
await this.store.updateTask(task.id, {
|
|
90778
|
+
status: null,
|
|
90779
|
+
error: null,
|
|
90780
|
+
mergeRetries: 0,
|
|
90781
|
+
worktree: null,
|
|
90782
|
+
branch: null,
|
|
90783
|
+
mergeDetails
|
|
90784
|
+
});
|
|
90785
|
+
await this.store.moveTask(task.id, "done");
|
|
90786
|
+
await this.cleanupInterruptedMergeArtifacts(task);
|
|
90787
|
+
const clearedDependents = [];
|
|
90788
|
+
for (const dep of blockedDependents) {
|
|
90789
|
+
try {
|
|
90790
|
+
await this.store.updateTask(dep.id, { blockedBy: null });
|
|
90791
|
+
await this.store.logEntry(dep.id, `Auto-recovered: cleared stale blockedBy ${task.id} after deadlock recovery`);
|
|
90792
|
+
clearedDependents.push(dep.id);
|
|
90793
|
+
} catch (depErr) {
|
|
90794
|
+
const depErrMessage = depErr instanceof Error ? depErr.message : String(depErr);
|
|
90795
|
+
log17.warn(`self-heal:deadlock-recovery-dependent-error ${JSON.stringify({ blockerTaskId: task.id, dependentTaskId: dep.id, error: depErrMessage })}`);
|
|
90796
|
+
}
|
|
90797
|
+
}
|
|
90798
|
+
await this.store.logEntry(
|
|
90799
|
+
task.id,
|
|
90800
|
+
`Auto-recovered: merge deadlock resolved via landed commit ${landedCommit.sha.slice(0, 8)}${clearedDependents.length > 0 ? `; cleared blockedBy on ${clearedDependents.join(", ")}` : ""}`
|
|
90801
|
+
);
|
|
90802
|
+
log17.log(`self-heal:deadlock-recovered ${JSON.stringify({ stuckTaskId: task.id, blockedTaskIds, attributedSha: landedCommit.sha, action: "reattributed" })}`);
|
|
90803
|
+
recovered++;
|
|
90804
|
+
} else {
|
|
90805
|
+
await this.store.updateTask(task.id, { paused: true });
|
|
90806
|
+
await this.store.logEntry(task.id, "merge-deadlock-detected: requires manual intervention \u2014 verified content not on main");
|
|
90807
|
+
log17.warn(`self-heal:deadlock-recovered ${JSON.stringify({ stuckTaskId: task.id, blockedTaskIds, attributedSha: null, action: "paused-for-manual" })}`);
|
|
90808
|
+
recovered++;
|
|
90809
|
+
}
|
|
90810
|
+
} catch (err) {
|
|
90811
|
+
const errorMessage = err instanceof Error ? err.message : String(err);
|
|
90812
|
+
log17.warn(`self-heal:deadlock-recovery-error ${JSON.stringify({ stuckTaskId: task.id, blockedTaskIds, error: errorMessage })}`);
|
|
90813
|
+
} finally {
|
|
90814
|
+
this.deadlockRecoveryCooldown.set(task.id, Date.now());
|
|
90815
|
+
}
|
|
90816
|
+
}
|
|
90817
|
+
return recovered;
|
|
90818
|
+
} catch (err) {
|
|
90819
|
+
const errorMessage = err instanceof Error ? err.message : String(err);
|
|
90820
|
+
log17.error(`Stuck merge deadlock recovery failed: ${errorMessage}`);
|
|
90821
|
+
return 0;
|
|
90822
|
+
}
|
|
90823
|
+
}
|
|
90122
90824
|
/**
|
|
90123
90825
|
* Recover retry-exhausted failed review tasks whose content already landed on
|
|
90124
90826
|
* the integration branch via a non-canonical merge lineage.
|
|
@@ -90257,7 +90959,7 @@ var init_self_healing = __esm({
|
|
|
90257
90959
|
return false;
|
|
90258
90960
|
}
|
|
90259
90961
|
const staleness = now - new Date(t.updatedAt).getTime();
|
|
90260
|
-
const hasWorktree = t.worktree &&
|
|
90962
|
+
const hasWorktree = t.worktree && existsSync32(t.worktree);
|
|
90261
90963
|
const graceMs = hasWorktree ? ORPHANED_WITH_WORKTREE_GRACE_MS : ORPHANED_EXECUTION_RECOVERY_GRACE_MS;
|
|
90262
90964
|
return staleness >= graceMs;
|
|
90263
90965
|
});
|
|
@@ -90266,7 +90968,7 @@ var init_self_healing = __esm({
|
|
|
90266
90968
|
let recovered = 0;
|
|
90267
90969
|
for (const task of orphaned) {
|
|
90268
90970
|
try {
|
|
90269
|
-
const hadWorktree = task.worktree &&
|
|
90971
|
+
const hadWorktree = task.worktree && existsSync32(task.worktree);
|
|
90270
90972
|
const reason = hadWorktree ? "worktree exists but no active session" : "missing worktree/session";
|
|
90271
90973
|
if (this.options.leaseManager && task.checkedOutBy) {
|
|
90272
90974
|
const leaseRecovered = await this.options.leaseManager.recoverAbandonedLease(
|
|
@@ -90569,7 +91271,7 @@ var init_self_healing = __esm({
|
|
|
90569
91271
|
}
|
|
90570
91272
|
}
|
|
90571
91273
|
async hasRecoverableGitWork(task) {
|
|
90572
|
-
if (task.worktree &&
|
|
91274
|
+
if (task.worktree && existsSync32(task.worktree)) {
|
|
90573
91275
|
try {
|
|
90574
91276
|
const { stdout: status } = await execAsync7("git status --porcelain", {
|
|
90575
91277
|
cwd: task.worktree,
|
|
@@ -90754,22 +91456,22 @@ var init_self_healing = __esm({
|
|
|
90754
91456
|
* tracks registered idle worktrees, never these orphans.
|
|
90755
91457
|
*/
|
|
90756
91458
|
async reapUnregisteredOrphans() {
|
|
90757
|
-
const worktreesDir =
|
|
90758
|
-
if (!
|
|
91459
|
+
const worktreesDir = join40(this.options.rootDir, ".worktrees");
|
|
91460
|
+
if (!existsSync32(worktreesDir)) return 0;
|
|
90759
91461
|
let dirs;
|
|
90760
91462
|
try {
|
|
90761
|
-
dirs = readdirSync5(worktreesDir, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) =>
|
|
91463
|
+
dirs = readdirSync5(worktreesDir, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => join40(worktreesDir, e.name));
|
|
90762
91464
|
} catch (err) {
|
|
90763
91465
|
log17.warn(`Failed to read .worktrees/ for unregistered orphan reap: ${err instanceof Error ? err.message : String(err)}`);
|
|
90764
91466
|
return 0;
|
|
90765
91467
|
}
|
|
90766
91468
|
if (dirs.length === 0) return 0;
|
|
90767
91469
|
const registered = await getRegisteredWorktreePaths(this.options.rootDir);
|
|
90768
|
-
const unregistered = dirs.filter((d) => !registered.has(
|
|
91470
|
+
const unregistered = dirs.filter((d) => !registered.has(resolve20(d)));
|
|
90769
91471
|
let cleaned = 0;
|
|
90770
91472
|
for (const path2 of unregistered) {
|
|
90771
|
-
const rel =
|
|
90772
|
-
if (rel === "" || rel.startsWith("..") ||
|
|
91473
|
+
const rel = relative10(worktreesDir, path2);
|
|
91474
|
+
if (rel === "" || rel.startsWith("..") || isAbsolute14(rel)) {
|
|
90773
91475
|
log17.warn(`Refusing to remove path outside .worktrees: ${path2}`);
|
|
90774
91476
|
continue;
|
|
90775
91477
|
}
|
|
@@ -90863,8 +91565,8 @@ var init_self_healing = __esm({
|
|
|
90863
91565
|
}
|
|
90864
91566
|
/** Remove oldest idle worktrees if total count exceeds 2× maxWorktrees. */
|
|
90865
91567
|
async enforceWorktreeCap() {
|
|
90866
|
-
const worktreesDir =
|
|
90867
|
-
if (!
|
|
91568
|
+
const worktreesDir = join40(this.options.rootDir, ".worktrees");
|
|
91569
|
+
if (!existsSync32(worktreesDir)) return;
|
|
90868
91570
|
try {
|
|
90869
91571
|
const settings = await this.store.getSettings();
|
|
90870
91572
|
const cap = (settings.maxWorktrees ?? 4) * 2;
|
|
@@ -91645,13 +92347,13 @@ var init_plugin_runner = __esm({
|
|
|
91645
92347
|
* Returns the result on success, throws on timeout.
|
|
91646
92348
|
*/
|
|
91647
92349
|
withTimeout(promise, ms, timeoutMessage) {
|
|
91648
|
-
return new Promise((
|
|
92350
|
+
return new Promise((resolve24, reject) => {
|
|
91649
92351
|
const timer = setTimeout(() => {
|
|
91650
92352
|
reject(new Error(timeoutMessage));
|
|
91651
92353
|
}, ms);
|
|
91652
92354
|
promise.then((result) => {
|
|
91653
92355
|
clearTimeout(timer);
|
|
91654
|
-
|
|
92356
|
+
resolve24(result);
|
|
91655
92357
|
}).catch((err) => {
|
|
91656
92358
|
clearTimeout(timer);
|
|
91657
92359
|
reject(err);
|
|
@@ -92558,7 +93260,7 @@ var init_in_process_runtime = __esm({
|
|
|
92558
93260
|
runtimeLog.log(
|
|
92559
93261
|
`Waiting for ${metrics.inFlightTasks} in-flight tasks to complete...`
|
|
92560
93262
|
);
|
|
92561
|
-
await new Promise((
|
|
93263
|
+
await new Promise((resolve24) => setTimeout(resolve24, 1e3));
|
|
92562
93264
|
}
|
|
92563
93265
|
const finalMetrics = this.getMetrics();
|
|
92564
93266
|
if (finalMetrics.inFlightTasks > 0) {
|
|
@@ -92988,13 +93690,13 @@ var init_ipc_host = __esm({
|
|
|
92988
93690
|
}
|
|
92989
93691
|
const id = generateCorrelationId();
|
|
92990
93692
|
const message = { type, id, payload };
|
|
92991
|
-
return new Promise((
|
|
93693
|
+
return new Promise((resolve24, reject) => {
|
|
92992
93694
|
const timeout = setTimeout(() => {
|
|
92993
93695
|
this.pendingCommands.delete(id);
|
|
92994
93696
|
reject(new Error(`Command ${type} timed out after ${timeoutMs ?? this.commandTimeoutMs}ms`));
|
|
92995
93697
|
}, timeoutMs ?? this.commandTimeoutMs);
|
|
92996
93698
|
this.pendingCommands.set(id, {
|
|
92997
|
-
resolve:
|
|
93699
|
+
resolve: resolve24,
|
|
92998
93700
|
reject,
|
|
92999
93701
|
timeout,
|
|
93000
93702
|
type
|
|
@@ -93072,7 +93774,7 @@ var init_ipc_host = __esm({
|
|
|
93072
93774
|
import { EventEmitter as EventEmitter20 } from "node:events";
|
|
93073
93775
|
import { fork } from "node:child_process";
|
|
93074
93776
|
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
93075
|
-
import { dirname as
|
|
93777
|
+
import { dirname as dirname12, join as join41 } from "node:path";
|
|
93076
93778
|
var HealthMonitor, ChildProcessRuntime;
|
|
93077
93779
|
var init_child_process_runtime = __esm({
|
|
93078
93780
|
"../engine/src/runtimes/child-process-runtime.ts"() {
|
|
@@ -93232,9 +93934,9 @@ var init_child_process_runtime = __esm({
|
|
|
93232
93934
|
*/
|
|
93233
93935
|
getWorkerPath() {
|
|
93234
93936
|
const isCompiled = !import.meta.url.endsWith(".ts");
|
|
93235
|
-
const currentDir =
|
|
93937
|
+
const currentDir = dirname12(fileURLToPath3(import.meta.url));
|
|
93236
93938
|
const workerFile = isCompiled ? "child-process-worker.js" : "child-process-worker.ts";
|
|
93237
|
-
return
|
|
93939
|
+
return join41(currentDir, workerFile);
|
|
93238
93940
|
}
|
|
93239
93941
|
/**
|
|
93240
93942
|
* Set up event forwarding from IPC host to runtime listeners.
|
|
@@ -93803,8 +94505,8 @@ var init_remote_node_client = __esm({
|
|
|
93803
94505
|
return error instanceof TypeError;
|
|
93804
94506
|
}
|
|
93805
94507
|
async sleep(ms) {
|
|
93806
|
-
await new Promise((
|
|
93807
|
-
setTimeout(
|
|
94508
|
+
await new Promise((resolve24) => {
|
|
94509
|
+
setTimeout(resolve24, ms);
|
|
93808
94510
|
});
|
|
93809
94511
|
}
|
|
93810
94512
|
};
|
|
@@ -94068,14 +94770,14 @@ var init_remote_node_runtime = __esm({
|
|
|
94068
94770
|
return error instanceof Error ? error : new Error(String(error));
|
|
94069
94771
|
}
|
|
94070
94772
|
async sleep(ms, signal) {
|
|
94071
|
-
await new Promise((
|
|
94773
|
+
await new Promise((resolve24) => {
|
|
94072
94774
|
const timeout = setTimeout(() => {
|
|
94073
94775
|
cleanup();
|
|
94074
|
-
|
|
94776
|
+
resolve24();
|
|
94075
94777
|
}, ms);
|
|
94076
94778
|
const onAbort = () => {
|
|
94077
94779
|
cleanup();
|
|
94078
|
-
|
|
94780
|
+
resolve24();
|
|
94079
94781
|
};
|
|
94080
94782
|
const cleanup = () => {
|
|
94081
94783
|
clearTimeout(timeout);
|
|
@@ -95026,10 +95728,10 @@ var init_tunnel_process_manager = __esm({
|
|
|
95026
95728
|
lastError: null
|
|
95027
95729
|
});
|
|
95028
95730
|
this.emitLog("info", "manager", `Stopping ${currentHandle.provider} tunnel (pid=${currentHandle.child.pid ?? "n/a"})`);
|
|
95029
|
-
this.activeStopPromise = new Promise((
|
|
95731
|
+
this.activeStopPromise = new Promise((resolve24) => {
|
|
95030
95732
|
const onClose = () => {
|
|
95031
95733
|
currentHandle.child.removeListener("close", onClose);
|
|
95032
|
-
|
|
95734
|
+
resolve24();
|
|
95033
95735
|
};
|
|
95034
95736
|
currentHandle.child.once("close", onClose);
|
|
95035
95737
|
killManagedProcess(currentHandle.child, "SIGTERM");
|
|
@@ -95302,17 +96004,25 @@ var init_project_engine = __esm({
|
|
|
95302
96004
|
onClosedPrFeedback: (taskId, prInfo, comments) => this.prCommentHandler.createFollowUpTask(taskId, prInfo, comments)
|
|
95303
96005
|
});
|
|
95304
96006
|
if (!this.options.skipNotifier) {
|
|
96007
|
+
const agentStore = this.runtime.getAgentStore();
|
|
96008
|
+
const agentNameResolver = agentStore ? async (agentId) => {
|
|
96009
|
+
const agent = await agentStore.getAgent(agentId);
|
|
96010
|
+
const name = typeof agent?.name === "string" ? agent.name.trim() : "";
|
|
96011
|
+
return name.length > 0 ? name : null;
|
|
96012
|
+
} : void 0;
|
|
95305
96013
|
this.notificationService = new NotificationService(store, {
|
|
95306
96014
|
projectId: this.options.projectId,
|
|
95307
96015
|
ntfyBaseUrl: this.options.ntfyBaseUrl,
|
|
95308
|
-
messageStore: this.runtime.getMessageStore()
|
|
96016
|
+
messageStore: this.runtime.getMessageStore(),
|
|
96017
|
+
agentNameResolver
|
|
95309
96018
|
});
|
|
95310
96019
|
await this.notificationService.start();
|
|
95311
96020
|
this.notifier = new NtfyNotifier(
|
|
95312
96021
|
store,
|
|
95313
96022
|
{
|
|
95314
96023
|
projectId: this.options.projectId,
|
|
95315
|
-
ntfyBaseUrl: this.options.ntfyBaseUrl
|
|
96024
|
+
ntfyBaseUrl: this.options.ntfyBaseUrl,
|
|
96025
|
+
agentNameResolver
|
|
95316
96026
|
},
|
|
95317
96027
|
this.notificationService
|
|
95318
96028
|
);
|
|
@@ -95670,12 +96380,12 @@ ${detail}`
|
|
|
95670
96380
|
*/
|
|
95671
96381
|
async onMerge(taskId) {
|
|
95672
96382
|
if (this.mergeActive.has(taskId)) {
|
|
95673
|
-
return new Promise((
|
|
95674
|
-
this.manualMergeResolvers.set(taskId, { resolve:
|
|
96383
|
+
return new Promise((resolve24, reject) => {
|
|
96384
|
+
this.manualMergeResolvers.set(taskId, { resolve: resolve24, reject });
|
|
95675
96385
|
});
|
|
95676
96386
|
}
|
|
95677
|
-
return new Promise((
|
|
95678
|
-
this.manualMergeResolvers.set(taskId, { resolve:
|
|
96387
|
+
return new Promise((resolve24, reject) => {
|
|
96388
|
+
this.manualMergeResolvers.set(taskId, { resolve: resolve24, reject });
|
|
95679
96389
|
this.internalEnqueueMerge(taskId);
|
|
95680
96390
|
});
|
|
95681
96391
|
}
|
|
@@ -95942,7 +96652,15 @@ ${detail}`
|
|
|
95942
96652
|
}
|
|
95943
96653
|
internalEnqueueMerge(taskId) {
|
|
95944
96654
|
if (this.shuttingDown) return;
|
|
95945
|
-
if (this.mergeActive.has(taskId))
|
|
96655
|
+
if (this.mergeActive.has(taskId)) {
|
|
96656
|
+
const isActuallyLive = this.mergeQueue.includes(taskId) || this.activeMergeTaskId === taskId;
|
|
96657
|
+
if (!isActuallyLive) {
|
|
96658
|
+
runtimeLog.warn(
|
|
96659
|
+
`internalEnqueueMerge(${taskId}): skipped \u2014 mergeActive entry is leaked (not queued, not active). reconcileStaleMergeActive() will clear it on the next sweep.`
|
|
96660
|
+
);
|
|
96661
|
+
}
|
|
96662
|
+
return;
|
|
96663
|
+
}
|
|
95946
96664
|
this.mergeActive.add(taskId);
|
|
95947
96665
|
this.mergeQueue.push(taskId);
|
|
95948
96666
|
void this.drainMergeQueue().catch((err) => {
|
|
@@ -96421,17 +97139,40 @@ ${detail}`
|
|
|
96421
97139
|
setTimeout(async () => {
|
|
96422
97140
|
try {
|
|
96423
97141
|
const latestTask = await store.getTask(task.id).catch(() => null);
|
|
96424
|
-
if (!latestTask)
|
|
96425
|
-
|
|
96426
|
-
|
|
96427
|
-
|
|
97142
|
+
if (!latestTask) {
|
|
97143
|
+
runtimeLog.warn(`Auto-merge handoff (${task.id}): task disappeared during grace period`);
|
|
97144
|
+
return;
|
|
97145
|
+
}
|
|
97146
|
+
if (latestTask.column !== "in-review") {
|
|
97147
|
+
runtimeLog.log(`Auto-merge handoff (${task.id}) skipped: column changed to ${latestTask.column}`);
|
|
97148
|
+
return;
|
|
97149
|
+
}
|
|
97150
|
+
if (latestTask.paused) {
|
|
97151
|
+
runtimeLog.log(`Auto-merge handoff (${task.id}) skipped: task paused`);
|
|
97152
|
+
return;
|
|
97153
|
+
}
|
|
97154
|
+
const blockerReason = this.options.getTaskMergeBlocker?.(latestTask);
|
|
97155
|
+
if (blockerReason) {
|
|
97156
|
+
runtimeLog.log(`Auto-merge handoff (${task.id}) skipped: ${blockerReason}`);
|
|
97157
|
+
return;
|
|
97158
|
+
}
|
|
96428
97159
|
const settings = await store.getSettings();
|
|
96429
|
-
if (settings.globalPause || settings.enginePaused)
|
|
96430
|
-
|
|
97160
|
+
if (settings.globalPause || settings.enginePaused) {
|
|
97161
|
+
runtimeLog.log(`Auto-merge handoff (${task.id}) skipped: ${settings.globalPause ? "globalPause" : "enginePaused"} active`);
|
|
97162
|
+
return;
|
|
97163
|
+
}
|
|
97164
|
+
if (!settings.autoMerge) {
|
|
97165
|
+
runtimeLog.log(`Auto-merge handoff (${task.id}) skipped: autoMerge disabled`);
|
|
97166
|
+
return;
|
|
97167
|
+
}
|
|
97168
|
+
if (this.mergeActive.has(task.id) && !this.mergeQueue.includes(task.id) && this.activeMergeTaskId !== task.id) {
|
|
97169
|
+
runtimeLog.warn(`Auto-merge handoff (${task.id}): clearing stale mergeActive before enqueue`);
|
|
97170
|
+
this.mergeActive.delete(task.id);
|
|
97171
|
+
}
|
|
96431
97172
|
this.internalEnqueueMerge(task.id);
|
|
96432
97173
|
} catch (err) {
|
|
96433
97174
|
runtimeLog.warn(
|
|
96434
|
-
`Auto-merge
|
|
97175
|
+
`Auto-merge handoff (${task.id}) failed: ${err instanceof Error ? err.message : String(err)}`
|
|
96435
97176
|
);
|
|
96436
97177
|
}
|
|
96437
97178
|
}, MERGE_HANDOFF_GRACE_MS);
|
|
@@ -99674,7 +100415,7 @@ function buildHermesArgs(prompt, settings, resumeSessionId) {
|
|
|
99674
100415
|
async function invokeHermesCli(prompt, settings, resumeSessionId, signal) {
|
|
99675
100416
|
const args = buildHermesArgs(prompt, settings, resumeSessionId);
|
|
99676
100417
|
const binary = resolveBinaryForSpawn(settings.binaryPath);
|
|
99677
|
-
return new Promise((
|
|
100418
|
+
return new Promise((resolve24, reject) => {
|
|
99678
100419
|
let settled = false;
|
|
99679
100420
|
const spawnEnv = { ...process.env, PYTHONUNBUFFERED: "1" };
|
|
99680
100421
|
if (settings.profile) {
|
|
@@ -99742,7 +100483,7 @@ ${combined}`));
|
|
|
99742
100483
|
return;
|
|
99743
100484
|
}
|
|
99744
100485
|
try {
|
|
99745
|
-
|
|
100486
|
+
resolve24(parseHermesOutput(stdout, stderr));
|
|
99746
100487
|
} catch (parseErr) {
|
|
99747
100488
|
reject(parseErr);
|
|
99748
100489
|
}
|
|
@@ -99772,37 +100513,37 @@ var init_cli_spawn = __esm({
|
|
|
99772
100513
|
});
|
|
99773
100514
|
|
|
99774
100515
|
// ../../plugins/fusion-plugin-hermes-runtime/dist/fusion-skill-install.js
|
|
99775
|
-
import { cpSync, existsSync as
|
|
100516
|
+
import { cpSync, existsSync as existsSync33, lstatSync as lstatSync2, mkdirSync as mkdirSync6, readFileSync as readFileSync12, readlinkSync, rmSync as rmSync4, symlinkSync, unlinkSync as unlinkSync2 } from "node:fs";
|
|
99776
100517
|
import { homedir as homedir7 } from "node:os";
|
|
99777
|
-
import { basename as basename8, dirname as
|
|
100518
|
+
import { basename as basename8, dirname as dirname13, join as join42, resolve as resolve21 } from "node:path";
|
|
99778
100519
|
import { fileURLToPath as fileURLToPath4 } from "node:url";
|
|
99779
100520
|
function resolveHermesHome(profile) {
|
|
99780
|
-
const base = process.env.HERMES_HOME ??
|
|
100521
|
+
const base = process.env.HERMES_HOME ?? join42(homedir7(), ".hermes");
|
|
99781
100522
|
if (!profile || profile === "default")
|
|
99782
100523
|
return base;
|
|
99783
|
-
return
|
|
100524
|
+
return join42(base, "profiles", profile);
|
|
99784
100525
|
}
|
|
99785
100526
|
function getFusionSkillSourceCandidates(moduleUrl = import.meta.url) {
|
|
99786
100527
|
const here = fileURLToPath4(moduleUrl);
|
|
99787
|
-
const moduleDir =
|
|
100528
|
+
const moduleDir = dirname13(here);
|
|
99788
100529
|
return [
|
|
99789
|
-
|
|
99790
|
-
|
|
99791
|
-
|
|
99792
|
-
|
|
100530
|
+
resolve21(moduleDir, "..", "..", "..", "..", "packages", "cli", "skill", FUSION_SKILL_NAME),
|
|
100531
|
+
resolve21(moduleDir, "..", "..", "..", "skill", FUSION_SKILL_NAME),
|
|
100532
|
+
resolve21(moduleDir, "..", "..", "skill", FUSION_SKILL_NAME),
|
|
100533
|
+
resolve21(moduleDir, "..", "..", "..", "..", "skill", FUSION_SKILL_NAME)
|
|
99793
100534
|
];
|
|
99794
100535
|
}
|
|
99795
100536
|
function resolveBundledFusionSkillSource() {
|
|
99796
100537
|
const candidates = getFusionSkillSourceCandidates();
|
|
99797
100538
|
for (const candidate of candidates) {
|
|
99798
|
-
if (
|
|
100539
|
+
if (existsSync33(join42(candidate, "SKILL.md")))
|
|
99799
100540
|
return candidate;
|
|
99800
100541
|
}
|
|
99801
100542
|
return null;
|
|
99802
100543
|
}
|
|
99803
100544
|
function installFusionSkillIntoHermesHome(options = {}) {
|
|
99804
100545
|
const sourceDir = options.sourceDir ?? resolveBundledFusionSkillSource();
|
|
99805
|
-
const targetDir =
|
|
100546
|
+
const targetDir = join42(resolveHermesHome(options.profile), "skills", FUSION_SKILL_NAME);
|
|
99806
100547
|
if (!sourceDir) {
|
|
99807
100548
|
return {
|
|
99808
100549
|
outcome: "warning",
|
|
@@ -99812,16 +100553,16 @@ function installFusionSkillIntoHermesHome(options = {}) {
|
|
|
99812
100553
|
};
|
|
99813
100554
|
}
|
|
99814
100555
|
try {
|
|
99815
|
-
mkdirSync6(
|
|
100556
|
+
mkdirSync6(dirname13(targetDir), { recursive: true });
|
|
99816
100557
|
let replaced = false;
|
|
99817
|
-
if (
|
|
99818
|
-
const
|
|
99819
|
-
if (
|
|
100558
|
+
if (existsSync33(targetDir) || isBrokenSymlink(targetDir)) {
|
|
100559
|
+
const stat9 = lstatSync2(targetDir);
|
|
100560
|
+
if (stat9.isSymbolicLink()) {
|
|
99820
100561
|
const currentTarget = safeReadlink(targetDir);
|
|
99821
|
-
if (currentTarget &&
|
|
100562
|
+
if (currentTarget && resolve21(dirname13(targetDir), currentTarget) === resolve21(sourceDir)) {
|
|
99822
100563
|
return { outcome: "already-installed", sourceDir, targetDir };
|
|
99823
100564
|
}
|
|
99824
|
-
if (!looksLikeFusionSkillTarget(
|
|
100565
|
+
if (!looksLikeFusionSkillTarget(resolve21(dirname13(targetDir), currentTarget ?? ""))) {
|
|
99825
100566
|
return {
|
|
99826
100567
|
outcome: "skipped",
|
|
99827
100568
|
sourceDir,
|
|
@@ -99884,15 +100625,15 @@ function safeReadlink(path2) {
|
|
|
99884
100625
|
}
|
|
99885
100626
|
function isBrokenSymlink(path2) {
|
|
99886
100627
|
try {
|
|
99887
|
-
const
|
|
99888
|
-
return
|
|
100628
|
+
const stat9 = lstatSync2(path2);
|
|
100629
|
+
return stat9.isSymbolicLink() && !existsSync33(path2);
|
|
99889
100630
|
} catch {
|
|
99890
100631
|
return false;
|
|
99891
100632
|
}
|
|
99892
100633
|
}
|
|
99893
100634
|
function looksLikePriorFusionInstall(path2) {
|
|
99894
|
-
const skillMd =
|
|
99895
|
-
if (!
|
|
100635
|
+
const skillMd = join42(path2, "SKILL.md");
|
|
100636
|
+
if (!existsSync33(skillMd))
|
|
99896
100637
|
return false;
|
|
99897
100638
|
try {
|
|
99898
100639
|
const body = readFileSync12(skillMd, "utf-8");
|
|
@@ -99906,7 +100647,7 @@ function looksLikeFusionSkillTarget(path2) {
|
|
|
99906
100647
|
return false;
|
|
99907
100648
|
if (basename8(path2).toLowerCase() === FUSION_SKILL_NAME)
|
|
99908
100649
|
return true;
|
|
99909
|
-
return
|
|
100650
|
+
return existsSync33(join42(path2, "SKILL.md"));
|
|
99910
100651
|
}
|
|
99911
100652
|
var FUSION_SKILL_NAME;
|
|
99912
100653
|
var init_fusion_skill_install = __esm({
|
|
@@ -100069,7 +100810,7 @@ var init_dist = __esm({
|
|
|
100069
100810
|
// ../../plugins/fusion-plugin-openclaw-runtime/dist/pi-module.js
|
|
100070
100811
|
import { spawn as spawn7 } from "node:child_process";
|
|
100071
100812
|
import { randomUUID as randomUUID18 } from "node:crypto";
|
|
100072
|
-
import { readFile as
|
|
100813
|
+
import { readFile as readFile20 } from "node:fs/promises";
|
|
100073
100814
|
function asString(v) {
|
|
100074
100815
|
return typeof v === "string" && v.trim() !== "" ? v.trim() : void 0;
|
|
100075
100816
|
}
|
|
@@ -100146,8 +100887,8 @@ function createCliSession(opts) {
|
|
|
100146
100887
|
};
|
|
100147
100888
|
}
|
|
100148
100889
|
async function configureOpenClawMcpServer(opts) {
|
|
100149
|
-
const serverValue = await
|
|
100150
|
-
await new Promise((
|
|
100890
|
+
const serverValue = await readFile20(opts.serverConfigPath, "utf-8");
|
|
100891
|
+
await new Promise((resolve24, reject) => {
|
|
100151
100892
|
const child = spawn7(opts.binaryPath, ["--no-color", "--profile", opts.profile, "mcp", "set", opts.serverName, serverValue], {
|
|
100152
100893
|
stdio: ["ignore", "pipe", "pipe"]
|
|
100153
100894
|
});
|
|
@@ -100158,7 +100899,7 @@ async function configureOpenClawMcpServer(opts) {
|
|
|
100158
100899
|
child.on("error", (err) => reject(new Error(`openclaw: failed to configure MCP server \u2014 ${err.message}`)));
|
|
100159
100900
|
child.on("close", (code) => {
|
|
100160
100901
|
if (code === 0) {
|
|
100161
|
-
|
|
100902
|
+
resolve24();
|
|
100162
100903
|
return;
|
|
100163
100904
|
}
|
|
100164
100905
|
reject(new Error(`openclaw: mcp set failed (${String(code)}): ${extractStderrError(stderr)}`));
|
|
@@ -100169,7 +100910,7 @@ async function promptCli(session, message, config, callbacks, signal) {
|
|
|
100169
100910
|
const args = buildOpenClawArgs(config, session, message);
|
|
100170
100911
|
const cb = { ...session.callbacks, ...callbacks };
|
|
100171
100912
|
cb.onToolStart?.("openclaw.agent", { sessionId: session.sessionId });
|
|
100172
|
-
return new Promise((
|
|
100913
|
+
return new Promise((resolve24, reject) => {
|
|
100173
100914
|
let settled = false;
|
|
100174
100915
|
const child = spawn7(config.binaryPath, args, {
|
|
100175
100916
|
stdio: ["ignore", "pipe", "pipe"]
|
|
@@ -100262,7 +101003,7 @@ async function promptCli(session, message, config, callbacks, signal) {
|
|
|
100262
101003
|
...metaError ? { error: metaError } : {},
|
|
100263
101004
|
...errorText.length > 0 ? { toolErrors: errorText } : {}
|
|
100264
101005
|
});
|
|
100265
|
-
|
|
101006
|
+
resolve24();
|
|
100266
101007
|
});
|
|
100267
101008
|
});
|
|
100268
101009
|
}
|
|
@@ -100285,9 +101026,9 @@ var init_pi_module = __esm({
|
|
|
100285
101026
|
// ../../plugins/fusion-plugin-openclaw-runtime/dist/mcp-config.js
|
|
100286
101027
|
import { writeFileSync as writeFileSync3 } from "node:fs";
|
|
100287
101028
|
import { tmpdir as tmpdir4 } from "node:os";
|
|
100288
|
-
import { join as
|
|
101029
|
+
import { join as join43 } from "node:path";
|
|
100289
101030
|
import { fileURLToPath as fileURLToPath5 } from "node:url";
|
|
100290
|
-
import { dirname as
|
|
101031
|
+
import { dirname as dirname14 } from "node:path";
|
|
100291
101032
|
function toolsToMcpToolDefs(tools) {
|
|
100292
101033
|
if (!Array.isArray(tools))
|
|
100293
101034
|
return [];
|
|
@@ -100299,16 +101040,16 @@ function toolsToMcpToolDefs(tools) {
|
|
|
100299
101040
|
}
|
|
100300
101041
|
function writeOpenClawMcpBridgeFiles(toolDefs, cacheKey) {
|
|
100301
101042
|
const suffix = cacheKey ? `${process.pid}-${cacheKey}` : `${process.pid}`;
|
|
100302
|
-
const schemaPath =
|
|
101043
|
+
const schemaPath = join43(tmpdir4(), `openclaw-runtime-mcp-schemas-${suffix}.json`);
|
|
100303
101044
|
writeFileSync3(schemaPath, JSON.stringify(toolDefs));
|
|
100304
101045
|
const __filename = fileURLToPath5(import.meta.url);
|
|
100305
|
-
const __dirname2 =
|
|
100306
|
-
const serverPath =
|
|
101046
|
+
const __dirname2 = dirname14(__filename);
|
|
101047
|
+
const serverPath = join43(__dirname2, "mcp-schema-server.cjs");
|
|
100307
101048
|
const serverConfig = {
|
|
100308
101049
|
command: "node",
|
|
100309
101050
|
args: [serverPath, schemaPath]
|
|
100310
101051
|
};
|
|
100311
|
-
const serverConfigPath =
|
|
101052
|
+
const serverConfigPath = join43(tmpdir4(), `openclaw-runtime-mcp-server-${suffix}.json`);
|
|
100312
101053
|
writeFileSync3(serverConfigPath, JSON.stringify(serverConfig));
|
|
100313
101054
|
return {
|
|
100314
101055
|
schemaPath,
|
|
@@ -100553,7 +101294,7 @@ var init_context = __esm({
|
|
|
100553
101294
|
|
|
100554
101295
|
// ../dashboard/src/github.ts
|
|
100555
101296
|
function delay2(ms) {
|
|
100556
|
-
return new Promise((
|
|
101297
|
+
return new Promise((resolve24) => setTimeout(resolve24, ms));
|
|
100557
101298
|
}
|
|
100558
101299
|
function normalizeCheckState(state) {
|
|
100559
101300
|
switch ((state ?? "").toLowerCase()) {
|
|
@@ -107071,10 +107812,10 @@ var require_browser3 = __commonJS({
|
|
|
107071
107812
|
text = canvas;
|
|
107072
107813
|
canvas = void 0;
|
|
107073
107814
|
}
|
|
107074
|
-
return new Promise(function(
|
|
107815
|
+
return new Promise(function(resolve24, reject) {
|
|
107075
107816
|
try {
|
|
107076
107817
|
const data = QRCode2.create(text, opts);
|
|
107077
|
-
|
|
107818
|
+
resolve24(renderFunc(data, canvas, opts));
|
|
107078
107819
|
} catch (e) {
|
|
107079
107820
|
reject(e);
|
|
107080
107821
|
}
|
|
@@ -107156,11 +107897,11 @@ var require_server = __commonJS({
|
|
|
107156
107897
|
}
|
|
107157
107898
|
function render(renderFunc, text, params) {
|
|
107158
107899
|
if (!params.cb) {
|
|
107159
|
-
return new Promise(function(
|
|
107900
|
+
return new Promise(function(resolve24, reject) {
|
|
107160
107901
|
try {
|
|
107161
107902
|
const data = QRCode2.create(text, params.opts);
|
|
107162
107903
|
return renderFunc(data, params.opts, function(err, data2) {
|
|
107163
|
-
return err ? reject(err) :
|
|
107904
|
+
return err ? reject(err) : resolve24(data2);
|
|
107164
107905
|
});
|
|
107165
107906
|
} catch (e) {
|
|
107166
107907
|
reject(e);
|
|
@@ -107621,7 +108362,7 @@ var init_exec_file = __esm({
|
|
|
107621
108362
|
|
|
107622
108363
|
// ../dashboard/src/routes/register-project-routes.ts
|
|
107623
108364
|
import * as fsPromises from "node:fs/promises";
|
|
107624
|
-
var access5,
|
|
108365
|
+
var access5, stat7, mkdir14, readdir9, rm3;
|
|
107625
108366
|
var init_register_project_routes = __esm({
|
|
107626
108367
|
"../dashboard/src/routes/register-project-routes.ts"() {
|
|
107627
108368
|
"use strict";
|
|
@@ -107631,10 +108372,10 @@ var init_register_project_routes = __esm({
|
|
|
107631
108372
|
init_project_store_resolver();
|
|
107632
108373
|
({
|
|
107633
108374
|
access: access5,
|
|
107634
|
-
stat:
|
|
107635
|
-
mkdir:
|
|
107636
|
-
readdir:
|
|
107637
|
-
rm:
|
|
108375
|
+
stat: stat7,
|
|
108376
|
+
mkdir: mkdir14,
|
|
108377
|
+
readdir: readdir9,
|
|
108378
|
+
rm: rm3
|
|
107638
108379
|
} = fsPromises);
|
|
107639
108380
|
}
|
|
107640
108381
|
});
|
|
@@ -107794,7 +108535,7 @@ var init_agent_generation = __esm({
|
|
|
107794
108535
|
|
|
107795
108536
|
// ../dashboard/src/routes/register-agent-import-export-generation-routes.ts
|
|
107796
108537
|
import * as fsPromises2 from "node:fs/promises";
|
|
107797
|
-
var mkdtemp, access6,
|
|
108538
|
+
var mkdtemp, access6, stat8, mkdir15, rm4, fsWriteFile;
|
|
107798
108539
|
var init_register_agent_import_export_generation_routes = __esm({
|
|
107799
108540
|
"../dashboard/src/routes/register-agent-import-export-generation-routes.ts"() {
|
|
107800
108541
|
"use strict";
|
|
@@ -107802,7 +108543,7 @@ var init_register_agent_import_export_generation_routes = __esm({
|
|
|
107802
108543
|
init_ai_session_diagnostics();
|
|
107803
108544
|
init_sse_buffer();
|
|
107804
108545
|
init_agent_generation();
|
|
107805
|
-
({ mkdtemp, access: access6, stat:
|
|
108546
|
+
({ mkdtemp, access: access6, stat: stat8, mkdir: mkdir15, rm: rm4, writeFile: fsWriteFile } = fsPromises2);
|
|
107806
108547
|
}
|
|
107807
108548
|
});
|
|
107808
108549
|
|
|
@@ -107889,7 +108630,7 @@ var init_droid_cli_probe = __esm({
|
|
|
107889
108630
|
// ../../plugins/fusion-plugin-cursor-runtime/src/cli-spawn.ts
|
|
107890
108631
|
import { spawn as spawn10 } from "node:child_process";
|
|
107891
108632
|
async function runCursorCommand(binary, args, timeoutMs) {
|
|
107892
|
-
return new Promise((
|
|
108633
|
+
return new Promise((resolve24) => {
|
|
107893
108634
|
const child = spawn10(binary, args, { stdio: ["ignore", "pipe", "pipe"] });
|
|
107894
108635
|
let stdout = "";
|
|
107895
108636
|
let stderr = "";
|
|
@@ -107898,7 +108639,7 @@ async function runCursorCommand(binary, args, timeoutMs) {
|
|
|
107898
108639
|
child.kill("SIGKILL");
|
|
107899
108640
|
} catch {
|
|
107900
108641
|
}
|
|
107901
|
-
|
|
108642
|
+
resolve24({ code: 124, stdout, stderr });
|
|
107902
108643
|
}, timeoutMs);
|
|
107903
108644
|
child.stdout?.on("data", (c) => {
|
|
107904
108645
|
stdout += c.toString("utf-8");
|
|
@@ -107908,11 +108649,11 @@ async function runCursorCommand(binary, args, timeoutMs) {
|
|
|
107908
108649
|
});
|
|
107909
108650
|
child.once("error", () => {
|
|
107910
108651
|
clearTimeout(timer);
|
|
107911
|
-
|
|
108652
|
+
resolve24({ code: 127, stdout, stderr });
|
|
107912
108653
|
});
|
|
107913
108654
|
child.once("close", (code) => {
|
|
107914
108655
|
clearTimeout(timer);
|
|
107915
|
-
|
|
108656
|
+
resolve24({ code, stdout, stderr });
|
|
107916
108657
|
});
|
|
107917
108658
|
});
|
|
107918
108659
|
}
|
|
@@ -108411,7 +109152,7 @@ async function spawnPaperclipCliJson(args, opts) {
|
|
|
108411
109152
|
}
|
|
108412
109153
|
const timeoutMs = opts.cliTimeoutMs ?? 15e3;
|
|
108413
109154
|
const label = ["paperclipai", ...args].join(" ");
|
|
108414
|
-
return new Promise((
|
|
109155
|
+
return new Promise((resolve24, reject) => {
|
|
108415
109156
|
let child;
|
|
108416
109157
|
try {
|
|
108417
109158
|
child = spawn13(bin, fullArgs, { stdio: ["ignore", "pipe", "pipe"] });
|
|
@@ -108457,7 +109198,7 @@ async function spawnPaperclipCliJson(args, opts) {
|
|
|
108457
109198
|
return;
|
|
108458
109199
|
}
|
|
108459
109200
|
try {
|
|
108460
|
-
|
|
109201
|
+
resolve24(JSON.parse(cleaned));
|
|
108461
109202
|
} catch {
|
|
108462
109203
|
reject(new Error(`${label} returned non-JSON output: ${cleaned.slice(0, 200)}`));
|
|
108463
109204
|
}
|
|
@@ -108527,7 +109268,7 @@ var init_paperclip_client = __esm({
|
|
|
108527
109268
|
// ../../plugins/fusion-plugin-paperclip-runtime/dist/runtime-adapter.js
|
|
108528
109269
|
import { randomUUID as randomUUID21 } from "node:crypto";
|
|
108529
109270
|
function sleep4(ms) {
|
|
108530
|
-
return new Promise((
|
|
109271
|
+
return new Promise((resolve24) => setTimeout(resolve24, ms));
|
|
108531
109272
|
}
|
|
108532
109273
|
function asString2(value) {
|
|
108533
109274
|
return typeof value === "string" ? value : void 0;
|
|
@@ -113048,7 +113789,7 @@ var init_auth_middleware = __esm({
|
|
|
113048
113789
|
|
|
113049
113790
|
// ../dashboard/src/server.ts
|
|
113050
113791
|
import express from "express";
|
|
113051
|
-
import { join as
|
|
113792
|
+
import { join as join44, dirname as dirname15 } from "node:path";
|
|
113052
113793
|
import { fileURLToPath as fileURLToPath6 } from "node:url";
|
|
113053
113794
|
function clearAiSessionCleanupInterval() {
|
|
113054
113795
|
if (!aiSessionCleanupIntervalHandle) {
|
|
@@ -113084,7 +113825,7 @@ var init_server = __esm({
|
|
|
113084
113825
|
init_auth_middleware();
|
|
113085
113826
|
init_remote_auth();
|
|
113086
113827
|
init_cli_package_version();
|
|
113087
|
-
__dirname =
|
|
113828
|
+
__dirname = dirname15(fileURLToPath6(import.meta.url));
|
|
113088
113829
|
MIN_AI_SESSION_TTL_MS = 10 * 60 * 1e3;
|
|
113089
113830
|
MAX_AI_SESSION_TTL_MS = 30 * 24 * 60 * 60 * 1e3;
|
|
113090
113831
|
MIN_AI_SESSION_CLEANUP_INTERVAL_MS = 60 * 1e3;
|
|
@@ -113156,7 +113897,7 @@ var init_src5 = __esm({
|
|
|
113156
113897
|
});
|
|
113157
113898
|
|
|
113158
113899
|
// src/project-context.ts
|
|
113159
|
-
import { resolve as
|
|
113900
|
+
import { resolve as resolve22, dirname as dirname16, basename as basename9 } from "node:path";
|
|
113160
113901
|
async function resolveProject(projectNameFlag, cwd = process.cwd(), globalDir) {
|
|
113161
113902
|
const central = new CentralCore(globalDir);
|
|
113162
113903
|
await central.init();
|
|
@@ -113232,10 +113973,10 @@ async function clearDefaultProject(globalDir) {
|
|
|
113232
113973
|
await globalStore.updateSettings(rest);
|
|
113233
113974
|
}
|
|
113234
113975
|
async function detectProjectFromCwd(cwd, central) {
|
|
113235
|
-
const startDir =
|
|
113976
|
+
const startDir = resolve22(cwd);
|
|
113236
113977
|
let currentDir = startDir;
|
|
113237
113978
|
while (true) {
|
|
113238
|
-
const kbPath =
|
|
113979
|
+
const kbPath = resolve22(currentDir, ".fusion", "fusion.db");
|
|
113239
113980
|
if (isValidSqliteDatabaseFile(kbPath)) {
|
|
113240
113981
|
const project = await central.getProjectByPath(currentDir);
|
|
113241
113982
|
if (project) {
|
|
@@ -113249,7 +113990,7 @@ async function detectProjectFromCwd(cwd, central) {
|
|
|
113249
113990
|
};
|
|
113250
113991
|
}
|
|
113251
113992
|
}
|
|
113252
|
-
const parentDir =
|
|
113993
|
+
const parentDir = dirname16(currentDir);
|
|
113253
113994
|
if (parentDir === currentDir) {
|
|
113254
113995
|
break;
|
|
113255
113996
|
}
|
|
@@ -113340,8 +114081,8 @@ __export(task_exports, {
|
|
|
113340
114081
|
runTaskUpdate: () => runTaskUpdate
|
|
113341
114082
|
});
|
|
113342
114083
|
import { createInterface as createInterface2 } from "node:readline/promises";
|
|
113343
|
-
import { watchFile, unwatchFile, statSync as statSync6, existsSync as
|
|
113344
|
-
import { basename as basename10, join as
|
|
114084
|
+
import { watchFile, unwatchFile, statSync as statSync6, existsSync as existsSync34, readFileSync as readFileSync13 } from "node:fs";
|
|
114085
|
+
import { basename as basename10, join as join45 } from "node:path";
|
|
113345
114086
|
function getGitHubIssueUrl(sourceMetadata) {
|
|
113346
114087
|
if (!sourceMetadata || typeof sourceMetadata !== "object") return void 0;
|
|
113347
114088
|
const issueUrl = sourceMetadata.issueUrl;
|
|
@@ -113517,10 +114258,10 @@ async function runTaskCreate(descriptionArg, attachFiles, depends, projectName,
|
|
|
113517
114258
|
}
|
|
113518
114259
|
console.log(` Path: .fusion/tasks/${task.id}/`);
|
|
113519
114260
|
if (attachFiles && attachFiles.length > 0) {
|
|
113520
|
-
const { readFile:
|
|
113521
|
-
const { basename: basename12, extname: extname4, resolve:
|
|
114261
|
+
const { readFile: readFile23 } = await import("node:fs/promises");
|
|
114262
|
+
const { basename: basename12, extname: extname4, resolve: resolve24 } = await import("node:path");
|
|
113522
114263
|
for (const filePath of attachFiles) {
|
|
113523
|
-
const resolvedPath =
|
|
114264
|
+
const resolvedPath = resolve24(filePath);
|
|
113524
114265
|
const filename = basename12(resolvedPath);
|
|
113525
114266
|
const ext = extname4(filename).toLowerCase();
|
|
113526
114267
|
const mimeType = MIME_TYPES[ext];
|
|
@@ -113530,7 +114271,7 @@ async function runTaskCreate(descriptionArg, attachFiles, depends, projectName,
|
|
|
113530
114271
|
}
|
|
113531
114272
|
let content;
|
|
113532
114273
|
try {
|
|
113533
|
-
content = await
|
|
114274
|
+
content = await readFile23(resolvedPath);
|
|
113534
114275
|
} catch {
|
|
113535
114276
|
console.error(` \u2717 Cannot read file: ${filePath}`);
|
|
113536
114277
|
continue;
|
|
@@ -113596,11 +114337,11 @@ async function runTaskLog(id, message, outcome, projectName) {
|
|
|
113596
114337
|
console.log(` \u2713 ${id}: logged "${message}"`);
|
|
113597
114338
|
console.log();
|
|
113598
114339
|
}
|
|
113599
|
-
function
|
|
114340
|
+
function formatTimestamp4(timestamp) {
|
|
113600
114341
|
return new Date(timestamp).toLocaleTimeString();
|
|
113601
114342
|
}
|
|
113602
114343
|
function formatLogEntry(entry) {
|
|
113603
|
-
const ts =
|
|
114344
|
+
const ts = formatTimestamp4(entry.timestamp);
|
|
113604
114345
|
const agent = entry.agent ? `[${entry.agent.toUpperCase()}] ` : "";
|
|
113605
114346
|
switch (entry.type) {
|
|
113606
114347
|
case "text":
|
|
@@ -113654,8 +114395,8 @@ async function runTaskLogs(id, options = {}, projectName) {
|
|
|
113654
114395
|
printEntries(filteredEntries);
|
|
113655
114396
|
if (options.follow) {
|
|
113656
114397
|
const projectPath = projectContext?.projectPath ?? process.cwd();
|
|
113657
|
-
const logPath =
|
|
113658
|
-
if (!
|
|
114398
|
+
const logPath = join45(projectPath, ".fusion", "tasks", id, "agent.log");
|
|
114399
|
+
if (!existsSync34(logPath)) {
|
|
113659
114400
|
console.log(`
|
|
113660
114401
|
Waiting for log file to be created...`);
|
|
113661
114402
|
}
|
|
@@ -113816,10 +114557,10 @@ async function runTaskMerge(id, projectName) {
|
|
|
113816
114557
|
}
|
|
113817
114558
|
}
|
|
113818
114559
|
async function runTaskAttach(id, filePath, projectName) {
|
|
113819
|
-
const { readFile:
|
|
114560
|
+
const { readFile: readFile23 } = await import("node:fs/promises");
|
|
113820
114561
|
const { basename: basename12, extname: extname4 } = await import("node:path");
|
|
113821
|
-
const { resolve:
|
|
113822
|
-
const resolvedPath =
|
|
114562
|
+
const { resolve: resolve24 } = await import("node:path");
|
|
114563
|
+
const resolvedPath = resolve24(filePath);
|
|
113823
114564
|
const filename = basename12(resolvedPath);
|
|
113824
114565
|
const ext = extname4(filename).toLowerCase();
|
|
113825
114566
|
const mimeType = MIME_TYPES[ext];
|
|
@@ -113830,7 +114571,7 @@ async function runTaskAttach(id, filePath, projectName) {
|
|
|
113830
114571
|
}
|
|
113831
114572
|
let content;
|
|
113832
114573
|
try {
|
|
113833
|
-
content = await
|
|
114574
|
+
content = await readFile23(resolvedPath);
|
|
113834
114575
|
} catch {
|
|
113835
114576
|
console.error(`Cannot read file: ${filePath}`);
|
|
113836
114577
|
process.exit(1);
|
|
@@ -114360,12 +115101,12 @@ async function promptText(question) {
|
|
|
114360
115101
|
console.log(" (Enter your response. Type DONE on its own line when finished):\n");
|
|
114361
115102
|
const rl = createInterface2({ input: process.stdin, output: process.stdout });
|
|
114362
115103
|
const lines = [];
|
|
114363
|
-
return new Promise((
|
|
115104
|
+
return new Promise((resolve24) => {
|
|
114364
115105
|
const askLine = () => {
|
|
114365
115106
|
rl.question(" ").then((line) => {
|
|
114366
115107
|
if (line.trim() === "DONE") {
|
|
114367
115108
|
rl.close();
|
|
114368
|
-
|
|
115109
|
+
resolve24(lines.join("\n"));
|
|
114369
115110
|
} else {
|
|
114370
115111
|
lines.push(line);
|
|
114371
115112
|
askLine();
|
|
@@ -114770,9 +115511,9 @@ async function runSkillsInstall(args, options) {
|
|
|
114770
115511
|
stdio: "inherit",
|
|
114771
115512
|
shell: true
|
|
114772
115513
|
});
|
|
114773
|
-
const exitCode = await new Promise((
|
|
115514
|
+
const exitCode = await new Promise((resolve24, reject) => {
|
|
114774
115515
|
child.on("exit", (code) => {
|
|
114775
|
-
|
|
115516
|
+
resolve24(code ?? 1);
|
|
114776
115517
|
});
|
|
114777
115518
|
child.on("error", (err) => {
|
|
114778
115519
|
reject(err);
|
|
@@ -114800,9 +115541,9 @@ init_gh_cli();
|
|
|
114800
115541
|
init_src2();
|
|
114801
115542
|
import { Type as Type8 } from "typebox";
|
|
114802
115543
|
import { StringEnum } from "@mariozechner/pi-ai";
|
|
114803
|
-
import { resolve as
|
|
114804
|
-
import { readFile as
|
|
114805
|
-
import { existsSync as
|
|
115544
|
+
import { resolve as resolve23, basename as basename11, extname as extname3, join as join46 } from "node:path";
|
|
115545
|
+
import { readFile as readFile22 } from "node:fs/promises";
|
|
115546
|
+
import { existsSync as existsSync35 } from "node:fs";
|
|
114806
115547
|
import { spawn as spawn12 } from "node:child_process";
|
|
114807
115548
|
var MIME_TYPES2 = {
|
|
114808
115549
|
".png": "image/png",
|
|
@@ -114820,14 +115561,14 @@ var MIME_TYPES2 = {
|
|
|
114820
115561
|
".xml": "application/xml"
|
|
114821
115562
|
};
|
|
114822
115563
|
function resolveProjectRoot2(cwd) {
|
|
114823
|
-
let current =
|
|
115564
|
+
let current = resolve23(cwd);
|
|
114824
115565
|
while (true) {
|
|
114825
|
-
if (
|
|
115566
|
+
if (existsSync35(join46(current, ".fusion"))) {
|
|
114826
115567
|
return current;
|
|
114827
115568
|
}
|
|
114828
|
-
const parent =
|
|
115569
|
+
const parent = resolve23(current, "..");
|
|
114829
115570
|
if (parent === current) {
|
|
114830
|
-
return
|
|
115571
|
+
return resolve23(cwd);
|
|
114831
115572
|
}
|
|
114832
115573
|
current = parent;
|
|
114833
115574
|
}
|
|
@@ -114843,7 +115584,7 @@ async function getStore2(cwd) {
|
|
|
114843
115584
|
return store;
|
|
114844
115585
|
}
|
|
114845
115586
|
function getFusionDir(cwd) {
|
|
114846
|
-
return
|
|
115587
|
+
return join46(resolveProjectRoot2(cwd), ".fusion");
|
|
114847
115588
|
}
|
|
114848
115589
|
async function validateAssignableAgentId(cwd, agentId, task, override = false) {
|
|
114849
115590
|
const { AgentStore: AgentStore2, isEphemeralAgent: isEphemeralAgent2 } = await Promise.resolve().then(() => (init_src(), src_exports));
|
|
@@ -114991,10 +115732,10 @@ async function sleepWithSignal(ms, signal) {
|
|
|
114991
115732
|
if (ms <= 0) {
|
|
114992
115733
|
return;
|
|
114993
115734
|
}
|
|
114994
|
-
await new Promise((
|
|
115735
|
+
await new Promise((resolve24, reject) => {
|
|
114995
115736
|
const timer = setTimeout(() => {
|
|
114996
115737
|
signal?.removeEventListener("abort", onAbort);
|
|
114997
|
-
|
|
115738
|
+
resolve24();
|
|
114998
115739
|
}, ms);
|
|
114999
115740
|
const onAbort = () => {
|
|
115000
115741
|
clearTimeout(timer);
|
|
@@ -115332,7 +116073,7 @@ Column: triage
|
|
|
115332
116073
|
path: Type8.String({ description: "Path to the file to attach" })
|
|
115333
116074
|
}),
|
|
115334
116075
|
async execute(_toolCallId, params, _signal, _onUpdate, ctx) {
|
|
115335
|
-
const filePath =
|
|
116076
|
+
const filePath = resolve23(ctx.cwd, params.path.replace(/^@/, ""));
|
|
115336
116077
|
const filename = basename11(filePath);
|
|
115337
116078
|
const ext = extname3(filename).toLowerCase();
|
|
115338
116079
|
const mimeType = MIME_TYPES2[ext];
|
|
@@ -115343,7 +116084,7 @@ Column: triage
|
|
|
115343
116084
|
}
|
|
115344
116085
|
let content;
|
|
115345
116086
|
try {
|
|
115346
|
-
content = await
|
|
116087
|
+
content = await readFile22(filePath);
|
|
115347
116088
|
} catch {
|
|
115348
116089
|
throw new Error(`Cannot read file: ${params.path}`);
|
|
115349
116090
|
}
|
|
@@ -117269,12 +118010,12 @@ ${lines.join("\n")}` }],
|
|
|
117269
118010
|
child.stderr?.on("data", (data) => {
|
|
117270
118011
|
stderr += data.toString();
|
|
117271
118012
|
});
|
|
117272
|
-
const exitCode = await new Promise((
|
|
118013
|
+
const exitCode = await new Promise((resolve24) => {
|
|
117273
118014
|
child.on("exit", (code) => {
|
|
117274
|
-
|
|
118015
|
+
resolve24(code ?? 1);
|
|
117275
118016
|
});
|
|
117276
118017
|
child.on("error", () => {
|
|
117277
|
-
|
|
118018
|
+
resolve24(1);
|
|
117278
118019
|
});
|
|
117279
118020
|
});
|
|
117280
118021
|
try {
|