@runfusion/fusion 0.14.2 → 0.15.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 +949 -138
- package/dist/client/assets/AgentDetailView-B1zViykq.js +18 -0
- package/dist/client/assets/{AgentsView-DkX0tzrN.js → AgentsView-Bl9JH5C8.js} +3 -3
- package/dist/client/assets/{ChatView-CEm2Hw6m.js → ChatView-liNErE53.js} +1 -1
- package/dist/client/assets/{DevServerView-Bumvo_ge.js → DevServerView-CV_PpbnZ.js} +1 -1
- package/dist/client/assets/{DirectoryPicker-CXN11cBp.js → DirectoryPicker-DPfkGnj5.js} +1 -1
- package/dist/client/assets/{DocumentsView-B71IqAxA.js → DocumentsView-CESb6RI7.js} +1 -1
- package/dist/client/assets/{InsightsView-Bs4Rldu6.js → InsightsView-BKhvyEyQ.js} +1 -1
- package/dist/client/assets/{MemoryView-Bs7b_L2Q.js → MemoryView-DB-l2miV.js} +1 -1
- package/dist/client/assets/{NodesView-BvAGTXbO.js → NodesView-DgTXO8mm.js} +1 -1
- package/dist/client/assets/{PiExtensionsManager-3Kcc4uhA.js → PiExtensionsManager-C4fTzemh.js} +1 -1
- package/dist/client/assets/{PluginManager-Ch-Xynlm.js → PluginManager-C2-dExUL.js} +1 -1
- package/dist/client/assets/{ResearchView-Bj6Saqf6.js → ResearchView-CkVwRDVA.js} +1 -1
- package/dist/client/assets/{RoadmapsView-9qT8Vwd0.js → RoadmapsView-Cu85_XrQ.js} +1 -1
- package/dist/client/assets/{SettingsModal-D4ERGQNQ.js → SettingsModal-BGnSAeqa.js} +1 -1
- package/dist/client/assets/SettingsModal-C0DokcId.js +31 -0
- package/dist/client/assets/{SetupWizardModal-Dv0rX2_o.js → SetupWizardModal-C_d9clJp.js} +1 -1
- package/dist/client/assets/{SkillMultiselect-CSkXQzdv.js → SkillMultiselect-DwGWYZi6.js} +1 -1
- package/dist/client/assets/{SkillsView-2srXMOzj.js → SkillsView-C096TB7i.js} +1 -1
- package/dist/client/assets/{TodoView-CxPPIvw2.js → TodoView-CUiAt2mR.js} +1 -1
- package/dist/client/assets/{folder-open-FA1PwpXV.js → folder-open-CKivQd8c.js} +1 -1
- package/dist/client/assets/index-B4StE1qN.js +662 -0
- package/dist/client/assets/index-DYJk0WDc.css +1 -0
- package/dist/client/assets/{list-checks-6EktkUso.js → list-checks-B3oufblU.js} +1 -1
- package/dist/client/assets/{star-B6Th07jw.js → star-damu_EYz.js} +1 -1
- package/dist/client/assets/{upload-BJwuErhV.js → upload-uH6CHlEw.js} +1 -1
- package/dist/client/assets/{users-BrnPTF8H.js → users-CUySbfji.js} +1 -1
- package/dist/client/index.html +2 -2
- package/dist/client/version.json +1 -1
- package/dist/extension.js +722 -120
- package/dist/pi-claude-cli/index.ts +2 -2
- package/dist/pi-claude-cli/package.json +1 -1
- package/package.json +1 -1
- package/dist/client/assets/AgentDetailView-C2Iik3Qf.js +0 -18
- package/dist/client/assets/SettingsModal-Zo5qDGOq.js +0 -31
- package/dist/client/assets/index-CEavim6l.js +0 -662
- package/dist/client/assets/index-D1gTSlYB.css +0 -1
package/dist/extension.js
CHANGED
|
@@ -75,6 +75,7 @@ var init_settings_schema = __esm({
|
|
|
75
75
|
favoriteModels: void 0,
|
|
76
76
|
openrouterModelSync: true,
|
|
77
77
|
updateCheckEnabled: true,
|
|
78
|
+
fnBinaryCheckEnabled: true,
|
|
78
79
|
updateCheckFrequency: "daily",
|
|
79
80
|
showGitHubStarButton: true,
|
|
80
81
|
modelOnboardingComplete: void 0,
|
|
@@ -239,7 +240,8 @@ var init_settings_schema = __esm({
|
|
|
239
240
|
maxPostReviewFixes: 1,
|
|
240
241
|
maxSpawnedAgentsPerParent: 5,
|
|
241
242
|
maxSpawnedAgentsGlobal: 20,
|
|
242
|
-
|
|
243
|
+
// Run maintenance (including WAL checkpointing) every 5 minutes by default.
|
|
244
|
+
maintenanceIntervalMs: 3e5,
|
|
243
245
|
autoArchiveDoneTasksEnabled: true,
|
|
244
246
|
autoArchiveDoneAfterMs: 48 * 60 * 60 * 1e3,
|
|
245
247
|
archiveAgentLogMode: "compact",
|
|
@@ -2650,6 +2652,7 @@ var init_sqlite_adapter = __esm({
|
|
|
2650
2652
|
// ../core/src/db.ts
|
|
2651
2653
|
import { isAbsolute, join as join2 } from "node:path";
|
|
2652
2654
|
import { mkdirSync, existsSync } from "node:fs";
|
|
2655
|
+
import { spawnSync } from "node:child_process";
|
|
2653
2656
|
function toJson(value) {
|
|
2654
2657
|
if (value === void 0 || value === null) return "[]";
|
|
2655
2658
|
if (Array.isArray(value) && value.length === 0) return "[]";
|
|
@@ -3281,15 +3284,24 @@ CREATE INDEX IF NOT EXISTS idxTodoItemsSortOrder ON todo_items(listId, sortOrder
|
|
|
3281
3284
|
Database = class {
|
|
3282
3285
|
db;
|
|
3283
3286
|
dbPath;
|
|
3287
|
+
inMemory;
|
|
3288
|
+
corruptionDetected = false;
|
|
3284
3289
|
/** Tracks transaction nesting depth for savepoint-based nested transactions. */
|
|
3285
3290
|
transactionDepth = 0;
|
|
3286
3291
|
_fts5Available;
|
|
3287
3292
|
constructor(fusionDir, options) {
|
|
3288
3293
|
const inMemory = options?.inMemory === true;
|
|
3294
|
+
this.inMemory = inMemory;
|
|
3289
3295
|
this.dbPath = inMemory ? ":memory:" : join2(fusionDir, "fusion.db");
|
|
3290
3296
|
if (!inMemory && !isAbsolute(fusionDir)) {
|
|
3291
3297
|
throw new Error(`[fusion] Database constructor requires an absolute fusionDir path, got: ${fusionDir}`);
|
|
3292
3298
|
}
|
|
3299
|
+
if (!inMemory && /\.fusion[\\/]\.fusion(?:[\\/]|$)/.test(fusionDir)) {
|
|
3300
|
+
throw new Error(
|
|
3301
|
+
`[fusion] Refusing to open Database at nested .fusion/.fusion path: ${fusionDir}
|
|
3302
|
+
This means a caller passed a .fusion directory where a project root was expected. Audit the call site for an extra \`join(rootDir, '.fusion')\` step.`
|
|
3303
|
+
);
|
|
3304
|
+
}
|
|
3293
3305
|
if (!inMemory && !existsSync(fusionDir)) {
|
|
3294
3306
|
mkdirSync(fusionDir, { recursive: true });
|
|
3295
3307
|
}
|
|
@@ -3301,8 +3313,13 @@ CREATE INDEX IF NOT EXISTS idxTodoItemsSortOrder ON todo_items(listId, sortOrder
|
|
|
3301
3313
|
}
|
|
3302
3314
|
if (!inMemory) {
|
|
3303
3315
|
this.db.exec("PRAGMA journal_mode = WAL");
|
|
3316
|
+
this.db.exec("PRAGMA busy_timeout = 5000");
|
|
3317
|
+
this.db.exec("PRAGMA synchronous = NORMAL");
|
|
3318
|
+
this.db.exec("PRAGMA wal_autocheckpoint = 100");
|
|
3319
|
+
this.db.exec("PRAGMA journal_size_limit = 4194304");
|
|
3320
|
+
} else {
|
|
3321
|
+
this.db.exec("PRAGMA busy_timeout = 5000");
|
|
3304
3322
|
}
|
|
3305
|
-
this.db.exec("PRAGMA busy_timeout = 5000");
|
|
3306
3323
|
this.db.exec("PRAGMA foreign_keys = ON");
|
|
3307
3324
|
this._fts5Available = probeFts5(this.db);
|
|
3308
3325
|
}
|
|
@@ -3383,6 +3400,35 @@ CREATE INDEX IF NOT EXISTS idxTodoItemsSortOrder ON todo_items(listId, sortOrder
|
|
|
3383
3400
|
return false;
|
|
3384
3401
|
}
|
|
3385
3402
|
}
|
|
3403
|
+
integrityCheck() {
|
|
3404
|
+
if (this.inMemory) {
|
|
3405
|
+
return { ok: true };
|
|
3406
|
+
}
|
|
3407
|
+
const rows = this.db.prepare("PRAGMA integrity_check(100)").all();
|
|
3408
|
+
const errors = rows.map((row) => row.integrity_check).filter((value) => typeof value === "string" && value !== "ok");
|
|
3409
|
+
if (errors.length > 0) {
|
|
3410
|
+
return { ok: false, errors };
|
|
3411
|
+
}
|
|
3412
|
+
return { ok: true };
|
|
3413
|
+
}
|
|
3414
|
+
recoverDatabase(outputPath) {
|
|
3415
|
+
if (this.inMemory) {
|
|
3416
|
+
return false;
|
|
3417
|
+
}
|
|
3418
|
+
const recoveredSql = spawnSync("sqlite3", ["-cmd", ".recover main", this.dbPath], {
|
|
3419
|
+
encoding: "utf-8",
|
|
3420
|
+
maxBuffer: 50 * 1024 * 1024
|
|
3421
|
+
});
|
|
3422
|
+
if (recoveredSql.status !== 0 || !recoveredSql.stdout) {
|
|
3423
|
+
return false;
|
|
3424
|
+
}
|
|
3425
|
+
const rebuilt = spawnSync("sqlite3", [outputPath], {
|
|
3426
|
+
input: recoveredSql.stdout,
|
|
3427
|
+
encoding: "utf-8",
|
|
3428
|
+
maxBuffer: 50 * 1024 * 1024
|
|
3429
|
+
});
|
|
3430
|
+
return rebuilt.status === 0;
|
|
3431
|
+
}
|
|
3386
3432
|
/**
|
|
3387
3433
|
* Initialize the database: create tables if they don't exist
|
|
3388
3434
|
* and seed meta values.
|
|
@@ -3401,6 +3447,11 @@ CREATE INDEX IF NOT EXISTS idxTodoItemsSortOrder ON todo_items(listId, sortOrder
|
|
|
3401
3447
|
this.db.exec(
|
|
3402
3448
|
`INSERT OR IGNORE INTO config (id, nextId, nextWorkflowStepId, settings, workflowSteps, updatedAt) VALUES (1, 1, 1, '${JSON.stringify(DEFAULT_PROJECT_SETTINGS)}', '[]', '${configNow}')`
|
|
3403
3449
|
);
|
|
3450
|
+
const integrity = this.integrityCheck();
|
|
3451
|
+
if (!integrity.ok) {
|
|
3452
|
+
this.corruptionDetected = true;
|
|
3453
|
+
console.warn("[fusion:db] Database integrity check FAILED \u2014 corruption detected");
|
|
3454
|
+
}
|
|
3404
3455
|
}
|
|
3405
3456
|
/**
|
|
3406
3457
|
* Run incremental schema migrations based on the stored schema version.
|
|
@@ -6016,6 +6067,7 @@ var init_agent_store = __esm({
|
|
|
6016
6067
|
};
|
|
6017
6068
|
const line = JSON.stringify(safeEntry) + "\n";
|
|
6018
6069
|
await appendFile(this.runLogPath(agentId, runId), line, "utf-8");
|
|
6070
|
+
this.emit("run:log", agentId, runId, safeEntry);
|
|
6019
6071
|
}
|
|
6020
6072
|
/**
|
|
6021
6073
|
* Read all log entries for a given run from its JSONL file.
|
|
@@ -19069,7 +19121,7 @@ __export(migration_exports, {
|
|
|
19069
19121
|
MigrationCoordinator: () => MigrationCoordinator,
|
|
19070
19122
|
ProjectRequiredError: () => ProjectRequiredError
|
|
19071
19123
|
});
|
|
19072
|
-
import { existsSync as existsSync9, readFileSync as readFileSync2 } from "node:fs";
|
|
19124
|
+
import { existsSync as existsSync9, readFileSync as readFileSync2, realpathSync as realpathSync2 } from "node:fs";
|
|
19073
19125
|
import { homedir as homedir2, tmpdir } from "node:os";
|
|
19074
19126
|
import { isAbsolute as isAbsolute3, join as join12, resolve as resolve5, basename as basename3, dirname as dirname4 } from "node:path";
|
|
19075
19127
|
function getHomeDir2() {
|
|
@@ -19170,12 +19222,30 @@ var init_migration = __esm({
|
|
|
19170
19222
|
const projects = [];
|
|
19171
19223
|
const visited = /* @__PURE__ */ new Set();
|
|
19172
19224
|
let current = resolve5(startDir);
|
|
19173
|
-
const home = getHomeDir2();
|
|
19225
|
+
const home = resolve5(getHomeDir2());
|
|
19174
19226
|
const root = dirname4(current) === current ? current : "/";
|
|
19175
19227
|
const systemTmp = resolve5(tmpdir());
|
|
19176
|
-
|
|
19228
|
+
const normalizePath2 = (path2) => {
|
|
19229
|
+
try {
|
|
19230
|
+
return realpathSync2(path2);
|
|
19231
|
+
} catch {
|
|
19232
|
+
return resolve5(path2);
|
|
19233
|
+
}
|
|
19234
|
+
};
|
|
19235
|
+
const normalizedHome = normalizePath2(home);
|
|
19236
|
+
const normalizedSystemTmp = normalizePath2(systemTmp);
|
|
19237
|
+
const normalizedStartDir = normalizePath2(current);
|
|
19238
|
+
while (true) {
|
|
19177
19239
|
if (visited.has(current)) break;
|
|
19178
19240
|
visited.add(current);
|
|
19241
|
+
const normalizedCurrent = normalizePath2(current);
|
|
19242
|
+
const isRootBoundary = current === root;
|
|
19243
|
+
const isHomeBoundary = normalizedCurrent === normalizedHome;
|
|
19244
|
+
const isTmpBoundary = normalizedCurrent === normalizedSystemTmp;
|
|
19245
|
+
const isStopBoundary = isRootBoundary || isHomeBoundary || isTmpBoundary;
|
|
19246
|
+
if (isRootBoundary || isTmpBoundary || isHomeBoundary && normalizedCurrent !== normalizedStartDir) {
|
|
19247
|
+
break;
|
|
19248
|
+
}
|
|
19179
19249
|
if (this.hasFusionProject(current)) {
|
|
19180
19250
|
const name = await this.generateProjectName(current);
|
|
19181
19251
|
projects.push({
|
|
@@ -19185,6 +19255,9 @@ var init_migration = __esm({
|
|
|
19185
19255
|
});
|
|
19186
19256
|
break;
|
|
19187
19257
|
}
|
|
19258
|
+
if (isStopBoundary) {
|
|
19259
|
+
break;
|
|
19260
|
+
}
|
|
19188
19261
|
const parent = dirname4(current);
|
|
19189
19262
|
if (parent === current) break;
|
|
19190
19263
|
current = parent;
|
|
@@ -34493,6 +34566,39 @@ ${task.description}
|
|
|
34493
34566
|
this.db.bumpLastModified();
|
|
34494
34567
|
this.emit("agent:log", entry);
|
|
34495
34568
|
}
|
|
34569
|
+
async appendAgentLogBatch(entries) {
|
|
34570
|
+
if (entries.length === 0) {
|
|
34571
|
+
return;
|
|
34572
|
+
}
|
|
34573
|
+
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
34574
|
+
const stmt = this.db.prepare(`
|
|
34575
|
+
INSERT INTO agentLogEntries (taskId, timestamp, text, type, detail, agent)
|
|
34576
|
+
VALUES (?, ?, ?, ?, ?, ?)
|
|
34577
|
+
`);
|
|
34578
|
+
this.db.transaction(() => {
|
|
34579
|
+
for (const entry of entries) {
|
|
34580
|
+
stmt.run(
|
|
34581
|
+
entry.taskId,
|
|
34582
|
+
timestamp,
|
|
34583
|
+
entry.text,
|
|
34584
|
+
entry.type,
|
|
34585
|
+
entry.detail ?? null,
|
|
34586
|
+
entry.agent ?? null
|
|
34587
|
+
);
|
|
34588
|
+
}
|
|
34589
|
+
});
|
|
34590
|
+
this.db.bumpLastModified();
|
|
34591
|
+
for (const entry of entries) {
|
|
34592
|
+
this.emit("agent:log", {
|
|
34593
|
+
timestamp,
|
|
34594
|
+
taskId: entry.taskId,
|
|
34595
|
+
text: entry.text,
|
|
34596
|
+
type: entry.type,
|
|
34597
|
+
...entry.detail !== void 0 && { detail: entry.detail },
|
|
34598
|
+
...entry.agent !== void 0 && { agent: entry.agent }
|
|
34599
|
+
});
|
|
34600
|
+
}
|
|
34601
|
+
}
|
|
34496
34602
|
mapAgentLogRow(row) {
|
|
34497
34603
|
return {
|
|
34498
34604
|
timestamp: row.timestamp,
|
|
@@ -36342,12 +36448,16 @@ var init_gh_cli = __esm({
|
|
|
36342
36448
|
|
|
36343
36449
|
// ../core/src/fn-binary.ts
|
|
36344
36450
|
import { spawn as spawn2 } from "node:child_process";
|
|
36345
|
-
import { platform as platform2 } from "node:os";
|
|
36451
|
+
import { platform as platform2, tmpdir as tmpdir2 } from "node:os";
|
|
36346
36452
|
function runProbe(command, args, timeoutMs) {
|
|
36347
36453
|
return new Promise((resolve19) => {
|
|
36348
36454
|
let stdout = "";
|
|
36349
36455
|
let stderr = "";
|
|
36350
|
-
const child = spawn2(command, args, {
|
|
36456
|
+
const child = spawn2(command, args, {
|
|
36457
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
36458
|
+
shell: false,
|
|
36459
|
+
cwd: tmpdir2()
|
|
36460
|
+
});
|
|
36351
36461
|
const timer = setTimeout(() => {
|
|
36352
36462
|
try {
|
|
36353
36463
|
child.kill("SIGKILL");
|
|
@@ -37478,6 +37588,20 @@ var init_plugin_loader = __esm({
|
|
|
37478
37588
|
}
|
|
37479
37589
|
return slots;
|
|
37480
37590
|
}
|
|
37591
|
+
/**
|
|
37592
|
+
* Get all top-level dashboard view definitions from loaded plugins.
|
|
37593
|
+
*/
|
|
37594
|
+
getPluginDashboardViews() {
|
|
37595
|
+
const views = [];
|
|
37596
|
+
for (const [pluginId, plugin4] of this.plugins) {
|
|
37597
|
+
if (plugin4.dashboardViews) {
|
|
37598
|
+
for (const view of plugin4.dashboardViews) {
|
|
37599
|
+
views.push({ pluginId, view });
|
|
37600
|
+
}
|
|
37601
|
+
}
|
|
37602
|
+
}
|
|
37603
|
+
return views;
|
|
37604
|
+
}
|
|
37481
37605
|
/**
|
|
37482
37606
|
* Get all runtime registrations from loaded plugins.
|
|
37483
37607
|
* Returns plugin ownership metadata along with the runtime registration.
|
|
@@ -37665,7 +37789,7 @@ async function syncBackupAutomation(automationStore, settings) {
|
|
|
37665
37789
|
if (!AutomationStore2.isValidCron(schedule)) {
|
|
37666
37790
|
throw new Error(`Invalid backup schedule: ${schedule}`);
|
|
37667
37791
|
}
|
|
37668
|
-
const command = "
|
|
37792
|
+
const command = "fn backup --create";
|
|
37669
37793
|
if (existingSchedule) {
|
|
37670
37794
|
return await automationStore.updateSchedule(existingSchedule.id, {
|
|
37671
37795
|
scheduleType: "custom",
|
|
@@ -37698,7 +37822,7 @@ async function syncBackupRoutine(routineStore, settings) {
|
|
|
37698
37822
|
if (!RoutineStore2.isValidCron(schedule)) {
|
|
37699
37823
|
throw new Error(`Invalid backup schedule: ${schedule}`);
|
|
37700
37824
|
}
|
|
37701
|
-
const command = "
|
|
37825
|
+
const command = "fn backup --create";
|
|
37702
37826
|
const input = {
|
|
37703
37827
|
name: BACKUP_SCHEDULE_NAME,
|
|
37704
37828
|
description: "Automatic database backup based on project settings",
|
|
@@ -49573,7 +49697,7 @@ var require_dist3 = __commonJS({
|
|
|
49573
49697
|
|
|
49574
49698
|
// ../core/src/agent-companies-parser.ts
|
|
49575
49699
|
import { existsSync as existsSync17, mkdtempSync, readdirSync as readdirSync2, readFileSync as readFileSync4, rmSync, statSync as statSync4 } from "node:fs";
|
|
49576
|
-
import { tmpdir as
|
|
49700
|
+
import { tmpdir as tmpdir3 } from "node:os";
|
|
49577
49701
|
import { join as join20, resolve as resolve9 } from "node:path";
|
|
49578
49702
|
function slugifyAgentReference(value) {
|
|
49579
49703
|
return value.trim().toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/-+/g, "-").replace(/^-+|-+$/g, "");
|
|
@@ -49896,7 +50020,7 @@ async function extractTarArchive(archivePath, outputDir) {
|
|
|
49896
50020
|
}
|
|
49897
50021
|
async function parseCompanyArchive(archivePath) {
|
|
49898
50022
|
const resolvedArchivePath = resolve9(archivePath);
|
|
49899
|
-
const tempDir = mkdtempSync(join20(
|
|
50023
|
+
const tempDir = mkdtempSync(join20(tmpdir3(), "agent-companies-"));
|
|
49900
50024
|
try {
|
|
49901
50025
|
if (resolvedArchivePath.endsWith(".tar.gz") || resolvedArchivePath.endsWith(".tgz")) {
|
|
49902
50026
|
await extractTarArchive(resolvedArchivePath, tempDir);
|
|
@@ -51216,18 +51340,21 @@ function summarizeToolArgs(name, args) {
|
|
|
51216
51340
|
}
|
|
51217
51341
|
return void 0;
|
|
51218
51342
|
}
|
|
51219
|
-
var FLUSH_SIZE_BYTES, FLUSH_INTERVAL_MS, AgentLogger;
|
|
51343
|
+
var FLUSH_SIZE_BYTES, FLUSH_INTERVAL_MS, ENTRY_BATCH_SIZE, AgentLogger;
|
|
51220
51344
|
var init_agent_logger = __esm({
|
|
51221
51345
|
"../engine/src/agent-logger.ts"() {
|
|
51222
51346
|
"use strict";
|
|
51223
51347
|
init_logger2();
|
|
51224
51348
|
FLUSH_SIZE_BYTES = 1024;
|
|
51225
51349
|
FLUSH_INTERVAL_MS = 500;
|
|
51350
|
+
ENTRY_BATCH_SIZE = 50;
|
|
51226
51351
|
AgentLogger = class {
|
|
51227
51352
|
textBuffer = "";
|
|
51228
51353
|
thinkingBuffer = "";
|
|
51229
51354
|
flushTimer = null;
|
|
51230
51355
|
thinkingFlushTimer = null;
|
|
51356
|
+
entryFlushTimer = null;
|
|
51357
|
+
pendingEntries = [];
|
|
51231
51358
|
flushSizeBytes;
|
|
51232
51359
|
flushIntervalMs;
|
|
51233
51360
|
store;
|
|
@@ -51332,8 +51459,13 @@ var init_agent_logger = __esm({
|
|
|
51332
51459
|
clearTimeout(this.thinkingFlushTimer);
|
|
51333
51460
|
this.thinkingFlushTimer = null;
|
|
51334
51461
|
}
|
|
51462
|
+
if (this.entryFlushTimer) {
|
|
51463
|
+
clearTimeout(this.entryFlushTimer);
|
|
51464
|
+
this.entryFlushTimer = null;
|
|
51465
|
+
}
|
|
51335
51466
|
await this.flushTextBuffer();
|
|
51336
51467
|
await this.flushThinkingBuffer();
|
|
51468
|
+
await this.flushPendingEntries();
|
|
51337
51469
|
}
|
|
51338
51470
|
// ── Internal helpers ───────────────────────────────────────────────
|
|
51339
51471
|
/**
|
|
@@ -51342,7 +51474,7 @@ var init_agent_logger = __esm({
|
|
|
51342
51474
|
* When only `appendLogCb` is set (no store/taskId), only the callback is used.
|
|
51343
51475
|
* @param storeWarnMsg - Warning message prefix used when the task-store write fails.
|
|
51344
51476
|
*/
|
|
51345
|
-
writeEntry(text, type, detail,
|
|
51477
|
+
writeEntry(text, type, detail, _storeWarnMsg, immediate = false) {
|
|
51346
51478
|
const entry = {
|
|
51347
51479
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
51348
51480
|
taskId: this.taskId,
|
|
@@ -51351,72 +51483,38 @@ var init_agent_logger = __esm({
|
|
|
51351
51483
|
...detail !== void 0 && { detail },
|
|
51352
51484
|
...this.agent !== void 0 && { agent: this.agent }
|
|
51353
51485
|
};
|
|
51354
|
-
|
|
51355
|
-
|
|
51356
|
-
|
|
51357
|
-
|
|
51486
|
+
this.pendingEntries.push(entry);
|
|
51487
|
+
if (immediate || type !== "text" && type !== "thinking") {
|
|
51488
|
+
if (this.entryFlushTimer) {
|
|
51489
|
+
clearTimeout(this.entryFlushTimer);
|
|
51490
|
+
this.entryFlushTimer = null;
|
|
51491
|
+
}
|
|
51492
|
+
void this.flushPendingEntries();
|
|
51493
|
+
return;
|
|
51358
51494
|
}
|
|
51359
|
-
if (this.
|
|
51360
|
-
this.
|
|
51361
|
-
this.
|
|
51362
|
-
|
|
51495
|
+
if (this.pendingEntries.length >= ENTRY_BATCH_SIZE) {
|
|
51496
|
+
if (this.entryFlushTimer) {
|
|
51497
|
+
clearTimeout(this.entryFlushTimer);
|
|
51498
|
+
this.entryFlushTimer = null;
|
|
51499
|
+
}
|
|
51500
|
+
void this.flushPendingEntries();
|
|
51501
|
+
return;
|
|
51363
51502
|
}
|
|
51503
|
+
this.scheduleEntryFlush();
|
|
51364
51504
|
}
|
|
51365
51505
|
flushTextBuffer() {
|
|
51366
51506
|
if (this.textBuffer.length === 0) return Promise.resolve();
|
|
51367
51507
|
const chunk = this.textBuffer;
|
|
51368
51508
|
this.textBuffer = "";
|
|
51369
|
-
|
|
51370
|
-
|
|
51371
|
-
taskId: this.taskId,
|
|
51372
|
-
text: chunk,
|
|
51373
|
-
type: "text",
|
|
51374
|
-
...this.agent !== void 0 && { agent: this.agent }
|
|
51375
|
-
};
|
|
51376
|
-
const promises = [];
|
|
51377
|
-
if (this.store && this.taskId) {
|
|
51378
|
-
promises.push(
|
|
51379
|
-
this.store.appendAgentLog(this.taskId, chunk, "text", void 0, this.agent).catch((err) => {
|
|
51380
|
-
this.log.warn(`Failed to flush text buffer for ${this.taskId}: ${err instanceof Error ? err.message : String(err)}`);
|
|
51381
|
-
})
|
|
51382
|
-
);
|
|
51383
|
-
}
|
|
51384
|
-
if (this.appendLogCb) {
|
|
51385
|
-
promises.push(
|
|
51386
|
-
this.appendLogCb(entry).catch((err) => {
|
|
51387
|
-
this.log.warn(`appendLog callback failed for text flush: ${err instanceof Error ? err.message : String(err)}`);
|
|
51388
|
-
})
|
|
51389
|
-
);
|
|
51390
|
-
}
|
|
51391
|
-
return Promise.all(promises).then(() => void 0);
|
|
51509
|
+
this.writeEntry(chunk, "text", void 0, `Failed to flush text buffer for ${this.taskId}`, true);
|
|
51510
|
+
return this.flushPendingEntries();
|
|
51392
51511
|
}
|
|
51393
51512
|
flushThinkingBuffer() {
|
|
51394
51513
|
if (this.thinkingBuffer.length === 0) return Promise.resolve();
|
|
51395
51514
|
const chunk = this.thinkingBuffer;
|
|
51396
51515
|
this.thinkingBuffer = "";
|
|
51397
|
-
|
|
51398
|
-
|
|
51399
|
-
taskId: this.taskId,
|
|
51400
|
-
text: chunk,
|
|
51401
|
-
type: "thinking",
|
|
51402
|
-
...this.agent !== void 0 && { agent: this.agent }
|
|
51403
|
-
};
|
|
51404
|
-
const promises = [];
|
|
51405
|
-
if (this.store && this.taskId) {
|
|
51406
|
-
promises.push(
|
|
51407
|
-
this.store.appendAgentLog(this.taskId, chunk, "thinking", void 0, this.agent).catch((err) => {
|
|
51408
|
-
this.log.warn(`Failed to flush thinking buffer for ${this.taskId}: ${err instanceof Error ? err.message : String(err)}`);
|
|
51409
|
-
})
|
|
51410
|
-
);
|
|
51411
|
-
}
|
|
51412
|
-
if (this.appendLogCb) {
|
|
51413
|
-
promises.push(
|
|
51414
|
-
this.appendLogCb(entry).catch((err) => {
|
|
51415
|
-
this.log.warn(`appendLog callback failed for thinking flush: ${err instanceof Error ? err.message : String(err)}`);
|
|
51416
|
-
})
|
|
51417
|
-
);
|
|
51418
|
-
}
|
|
51419
|
-
return Promise.all(promises).then(() => void 0);
|
|
51516
|
+
this.writeEntry(chunk, "thinking", void 0, `Failed to flush thinking buffer for ${this.taskId}`, true);
|
|
51517
|
+
return this.flushPendingEntries();
|
|
51420
51518
|
}
|
|
51421
51519
|
scheduleFlush() {
|
|
51422
51520
|
if (this.flushTimer) return;
|
|
@@ -51432,6 +51530,52 @@ var init_agent_logger = __esm({
|
|
|
51432
51530
|
this.flushThinkingBuffer();
|
|
51433
51531
|
}, this.flushIntervalMs);
|
|
51434
51532
|
}
|
|
51533
|
+
scheduleEntryFlush() {
|
|
51534
|
+
if (this.entryFlushTimer) return;
|
|
51535
|
+
this.entryFlushTimer = setTimeout(() => {
|
|
51536
|
+
this.entryFlushTimer = null;
|
|
51537
|
+
void this.flushPendingEntries();
|
|
51538
|
+
}, this.flushIntervalMs);
|
|
51539
|
+
}
|
|
51540
|
+
async flushPendingEntries() {
|
|
51541
|
+
if (this.pendingEntries.length === 0) {
|
|
51542
|
+
return;
|
|
51543
|
+
}
|
|
51544
|
+
const entries = this.pendingEntries;
|
|
51545
|
+
this.pendingEntries = [];
|
|
51546
|
+
if (this.store && this.taskId) {
|
|
51547
|
+
if (typeof this.store.appendAgentLogBatch === "function") {
|
|
51548
|
+
await this.store.appendAgentLogBatch(
|
|
51549
|
+
entries.map((entry) => ({
|
|
51550
|
+
taskId: entry.taskId,
|
|
51551
|
+
text: entry.text,
|
|
51552
|
+
type: entry.type,
|
|
51553
|
+
detail: entry.detail,
|
|
51554
|
+
agent: entry.agent
|
|
51555
|
+
}))
|
|
51556
|
+
).catch((err) => {
|
|
51557
|
+
this.log.warn(`Failed to flush agent log batch for ${this.taskId}: ${err instanceof Error ? err.message : String(err)}`);
|
|
51558
|
+
});
|
|
51559
|
+
} else {
|
|
51560
|
+
await Promise.all(
|
|
51561
|
+
entries.map(
|
|
51562
|
+
(entry) => this.store.appendAgentLog(entry.taskId, entry.text, entry.type, entry.detail, entry.agent).catch((err) => {
|
|
51563
|
+
this.log.warn(`Failed to flush agent log entry for ${this.taskId}: ${err instanceof Error ? err.message : String(err)}`);
|
|
51564
|
+
})
|
|
51565
|
+
)
|
|
51566
|
+
);
|
|
51567
|
+
}
|
|
51568
|
+
}
|
|
51569
|
+
if (this.appendLogCb) {
|
|
51570
|
+
await Promise.all(
|
|
51571
|
+
entries.map(
|
|
51572
|
+
(entry) => this.appendLogCb(entry).catch((err) => {
|
|
51573
|
+
this.log.warn(`appendLog callback failed for entry (${entry.type}): ${err instanceof Error ? err.message : String(err)}`);
|
|
51574
|
+
})
|
|
51575
|
+
)
|
|
51576
|
+
);
|
|
51577
|
+
}
|
|
51578
|
+
}
|
|
51435
51579
|
};
|
|
51436
51580
|
}
|
|
51437
51581
|
});
|
|
@@ -65008,6 +65152,20 @@ The tool prevents your session from being killed by the inactivity watchdog duri
|
|
|
65008
65152
|
);
|
|
65009
65153
|
this.activeStepExecutors.delete(task.id);
|
|
65010
65154
|
}
|
|
65155
|
+
if (this.activeWorkflowStepSessions.has(task.id)) {
|
|
65156
|
+
executorLog.log(`${task.id} moved from in-progress to ${to} \u2014 terminating workflow step session`);
|
|
65157
|
+
this.pausedAborted.add(task.id);
|
|
65158
|
+
this.options.stuckTaskDetector?.untrackTask(task.id);
|
|
65159
|
+
const workflowSession = this.activeWorkflowStepSessions.get(task.id);
|
|
65160
|
+
const sessionWithAbort = workflowSession;
|
|
65161
|
+
if (typeof sessionWithAbort.abort === "function") {
|
|
65162
|
+
void sessionWithAbort.abort().catch((err) => {
|
|
65163
|
+
executorLog.warn(`Failed to abort workflow step session for ${task.id}: ${err}`);
|
|
65164
|
+
});
|
|
65165
|
+
}
|
|
65166
|
+
workflowSession.dispose();
|
|
65167
|
+
this.activeWorkflowStepSessions.delete(task.id);
|
|
65168
|
+
}
|
|
65011
65169
|
this.disposeSubagentsForTask(task.id, `parent moved from in-progress to ${to}`);
|
|
65012
65170
|
this.loopRecoveryState.delete(task.id);
|
|
65013
65171
|
this.spawnedAgents.delete(task.id);
|
|
@@ -65040,8 +65198,42 @@ The tool prevents your session from being killed by the inactivity watchdog duri
|
|
|
65040
65198
|
this.disposeSubagentsForTask(task.id, "task paused");
|
|
65041
65199
|
return;
|
|
65042
65200
|
}
|
|
65043
|
-
if (
|
|
65201
|
+
if (task.paused && this.activeWorkflowStepSessions.has(task.id)) {
|
|
65202
|
+
executorLog.log(`Pausing ${task.id} \u2014 terminating workflow step session`);
|
|
65203
|
+
this.pausedAborted.add(task.id);
|
|
65204
|
+
this.options.stuckTaskDetector?.untrackTask(task.id);
|
|
65205
|
+
const workflowSession = this.activeWorkflowStepSessions.get(task.id);
|
|
65206
|
+
const sessionWithAbort = workflowSession;
|
|
65207
|
+
if (typeof sessionWithAbort.abort === "function") {
|
|
65208
|
+
await sessionWithAbort.abort().catch(
|
|
65209
|
+
(err) => executorLog.warn(`Failed to abort workflow step session for pause ${task.id}: ${err}`)
|
|
65210
|
+
);
|
|
65211
|
+
}
|
|
65212
|
+
workflowSession.dispose();
|
|
65213
|
+
this.activeWorkflowStepSessions.delete(task.id);
|
|
65214
|
+
this.loopRecoveryState.delete(task.id);
|
|
65215
|
+
this.spawnedAgents.delete(task.id);
|
|
65216
|
+
this.stuckAborted.delete(task.id);
|
|
65217
|
+
this.disposeSubagentsForTask(task.id, "task paused");
|
|
65218
|
+
return;
|
|
65219
|
+
}
|
|
65220
|
+
if (!task.paused && task.column === "in-progress" && !this.activeSessions.has(task.id) && !this.activeStepExecutors.has(task.id) && !this.activeWorkflowStepSessions.has(task.id)) {
|
|
65044
65221
|
if (!this.executing.has(task.id) && !this.resumingUnpaused.has(task.id) && !this.recoveringCompleted.has(task.id)) {
|
|
65222
|
+
const pauseLabel = await this.getExecutionPauseLabel();
|
|
65223
|
+
if (pauseLabel) {
|
|
65224
|
+
executorLog.log(`Skipping unpause resume for ${task.id} \u2014 ${pauseLabel} active`);
|
|
65225
|
+
return;
|
|
65226
|
+
}
|
|
65227
|
+
if (this.isTaskWorkComplete(task) && !task.mergeDetails) {
|
|
65228
|
+
this.recoveringCompleted.add(task.id);
|
|
65229
|
+
executorLog.log(`${task.id} unpaused with completed work and no session \u2014 recovering directly to in-review`);
|
|
65230
|
+
void this.recoverCompletedTask(task).catch(
|
|
65231
|
+
(err) => executorLog.error(`Failed to recover completed unpaused task ${task.id}:`, err)
|
|
65232
|
+
).finally(() => {
|
|
65233
|
+
this.recoveringCompleted.delete(task.id);
|
|
65234
|
+
});
|
|
65235
|
+
return;
|
|
65236
|
+
}
|
|
65045
65237
|
this.resumingUnpaused.add(task.id);
|
|
65046
65238
|
executorLog.log(`Unpaused ${task.id} in-progress with no session \u2014 resuming execution`);
|
|
65047
65239
|
try {
|
|
@@ -65160,6 +65352,22 @@ The tool prevents your session from being killed by the inactivity watchdog duri
|
|
|
65160
65352
|
this.spawnedAgents.delete(taskId);
|
|
65161
65353
|
this.stuckAborted.delete(taskId);
|
|
65162
65354
|
}
|
|
65355
|
+
for (const [taskId, workflowSession] of this.activeWorkflowStepSessions) {
|
|
65356
|
+
executorLog.log(`Global pause \u2014 terminating workflow step session for ${taskId}`);
|
|
65357
|
+
this.pausedAborted.add(taskId);
|
|
65358
|
+
this.options.stuckTaskDetector?.untrackTask(taskId);
|
|
65359
|
+
const sessionWithAbort = workflowSession;
|
|
65360
|
+
if (typeof sessionWithAbort.abort === "function") {
|
|
65361
|
+
void sessionWithAbort.abort().catch((err) => {
|
|
65362
|
+
executorLog.warn(`Failed to abort workflow step session for ${taskId}: ${err}`);
|
|
65363
|
+
});
|
|
65364
|
+
}
|
|
65365
|
+
workflowSession.dispose();
|
|
65366
|
+
this.activeWorkflowStepSessions.delete(taskId);
|
|
65367
|
+
this.loopRecoveryState.delete(taskId);
|
|
65368
|
+
this.spawnedAgents.delete(taskId);
|
|
65369
|
+
this.stuckAborted.delete(taskId);
|
|
65370
|
+
}
|
|
65163
65371
|
}
|
|
65164
65372
|
});
|
|
65165
65373
|
}
|
|
@@ -65177,6 +65385,8 @@ The tool prevents your session from being killed by the inactivity watchdog duri
|
|
|
65177
65385
|
activeSessions = /* @__PURE__ */ new Map();
|
|
65178
65386
|
/** Active step-session executors per task (mutually exclusive with activeSessions). */
|
|
65179
65387
|
activeStepExecutors = /* @__PURE__ */ new Map();
|
|
65388
|
+
/** Active pre-merge workflow step sessions per task. */
|
|
65389
|
+
activeWorkflowStepSessions = /* @__PURE__ */ new Map();
|
|
65180
65390
|
/**
|
|
65181
65391
|
* Reviewer subagent sessions per task. Reviewers (`reviewer.ts`) create their
|
|
65182
65392
|
* own AgentSessions that aren't part of `activeSessions`/`activeStepExecutors`,
|
|
@@ -65223,6 +65433,69 @@ The tool prevents your session from being killed by the inactivity watchdog duri
|
|
|
65223
65433
|
await this.store.mergeTask(taskId);
|
|
65224
65434
|
return "merged";
|
|
65225
65435
|
}
|
|
65436
|
+
async getExecutionPauseLabel() {
|
|
65437
|
+
const settings = await this.store.getSettings();
|
|
65438
|
+
if (settings.globalPause) return "global pause";
|
|
65439
|
+
if (settings.enginePaused) return "engine pause";
|
|
65440
|
+
return null;
|
|
65441
|
+
}
|
|
65442
|
+
async shouldDeferCompletionForGlobalPause(taskId, context) {
|
|
65443
|
+
const settings = await this.store.getSettings();
|
|
65444
|
+
if (!settings.globalPause) {
|
|
65445
|
+
return false;
|
|
65446
|
+
}
|
|
65447
|
+
this.clearCompletedTaskWatchdog(taskId);
|
|
65448
|
+
executorLog.log(`${taskId}: completion handoff deferred \u2014 global pause active (${context})`);
|
|
65449
|
+
await this.store.logEntry(
|
|
65450
|
+
taskId,
|
|
65451
|
+
`Completion handoff deferred \u2014 global pause active (${context})`,
|
|
65452
|
+
void 0,
|
|
65453
|
+
this.currentRunContext
|
|
65454
|
+
).catch(() => void 0);
|
|
65455
|
+
return true;
|
|
65456
|
+
}
|
|
65457
|
+
async shouldDeferWorkflowStepCompletion(taskId, context) {
|
|
65458
|
+
let latestTask = null;
|
|
65459
|
+
try {
|
|
65460
|
+
latestTask = await this.store.getTask(taskId);
|
|
65461
|
+
} catch {
|
|
65462
|
+
latestTask = null;
|
|
65463
|
+
}
|
|
65464
|
+
if (latestTask?.paused || this.pausedAborted.has(taskId)) {
|
|
65465
|
+
this.clearCompletedTaskWatchdog(taskId);
|
|
65466
|
+
executorLog.log(`${taskId}: completion handoff deferred \u2014 task paused (${context})`);
|
|
65467
|
+
await this.store.logEntry(
|
|
65468
|
+
taskId,
|
|
65469
|
+
`Completion handoff deferred \u2014 task paused (${context})`,
|
|
65470
|
+
void 0,
|
|
65471
|
+
this.currentRunContext
|
|
65472
|
+
).catch(() => void 0);
|
|
65473
|
+
return true;
|
|
65474
|
+
}
|
|
65475
|
+
return this.shouldDeferCompletionForGlobalPause(taskId, context);
|
|
65476
|
+
}
|
|
65477
|
+
async parkTaskAfterWorkflowStepPause(taskId) {
|
|
65478
|
+
let latestTask = null;
|
|
65479
|
+
try {
|
|
65480
|
+
latestTask = await this.store.getTask(taskId);
|
|
65481
|
+
} catch {
|
|
65482
|
+
latestTask = null;
|
|
65483
|
+
}
|
|
65484
|
+
if (!latestTask?.paused) {
|
|
65485
|
+
return false;
|
|
65486
|
+
}
|
|
65487
|
+
executorLog.log(`${taskId}: workflow step interrupted by task pause \u2014 moving to todo`);
|
|
65488
|
+
await this.store.logEntry(
|
|
65489
|
+
taskId,
|
|
65490
|
+
"Execution paused during pre-merge workflow step \u2014 moved to todo",
|
|
65491
|
+
void 0,
|
|
65492
|
+
this.currentRunContext
|
|
65493
|
+
).catch(() => void 0);
|
|
65494
|
+
if (latestTask.column === "in-progress") {
|
|
65495
|
+
await this.store.moveTask(taskId, "todo", { preserveResumeState: true });
|
|
65496
|
+
}
|
|
65497
|
+
return true;
|
|
65498
|
+
}
|
|
65226
65499
|
/** Child agent sessions keyed by agent ID. Used for termination. */
|
|
65227
65500
|
childSessions = /* @__PURE__ */ new Map();
|
|
65228
65501
|
/** Total count of currently spawned agents (across all parents). */
|
|
@@ -65417,11 +65690,15 @@ The tool prevents your session from being killed by the inactivity watchdog duri
|
|
|
65417
65690
|
this.clearCompletedTaskWatchdog(taskId);
|
|
65418
65691
|
const handle = setTimeout(async () => {
|
|
65419
65692
|
this.completedTaskWatchdogs.delete(taskId);
|
|
65420
|
-
if (this.recoveringCompleted.has(taskId) || this.executing.has(taskId) || this.activeSessions.has(taskId) || this.activeStepExecutors.has(taskId) || this.resumingUnpaused.has(taskId)) {
|
|
65693
|
+
if (this.recoveringCompleted.has(taskId) || this.executing.has(taskId) || this.activeSessions.has(taskId) || this.activeStepExecutors.has(taskId) || this.activeWorkflowStepSessions.has(taskId) || this.resumingUnpaused.has(taskId)) {
|
|
65421
65694
|
return;
|
|
65422
65695
|
}
|
|
65423
65696
|
this.recoveringCompleted.add(taskId);
|
|
65424
65697
|
try {
|
|
65698
|
+
const pauseLabel = await this.getExecutionPauseLabel();
|
|
65699
|
+
if (pauseLabel) {
|
|
65700
|
+
return;
|
|
65701
|
+
}
|
|
65425
65702
|
let currentTask = null;
|
|
65426
65703
|
try {
|
|
65427
65704
|
currentTask = await this.store.getTask(taskId);
|
|
@@ -65467,6 +65744,11 @@ The tool prevents your session from being killed by the inactivity watchdog duri
|
|
|
65467
65744
|
* stuck.
|
|
65468
65745
|
*/
|
|
65469
65746
|
async performWorkflowRerunBounce(taskId, worktreePath, preserveResumeState = true) {
|
|
65747
|
+
const pauseLabel = await this.getExecutionPauseLabel();
|
|
65748
|
+
if (pauseLabel) {
|
|
65749
|
+
executorLog.log(`${taskId}: workflow rerun deferred \u2014 ${pauseLabel} active`);
|
|
65750
|
+
return "deferred-paused";
|
|
65751
|
+
}
|
|
65470
65752
|
if (this.workflowRerunPending.has(taskId)) {
|
|
65471
65753
|
executorLog.warn(`${taskId}: workflow rerun bounce already in flight \u2014 skipping re-entry`);
|
|
65472
65754
|
return "skipped-pending";
|
|
@@ -65477,6 +65759,10 @@ The tool prevents your session from being killed by the inactivity watchdog duri
|
|
|
65477
65759
|
if (!latestTask) {
|
|
65478
65760
|
throw new Error("task missing during workflow rerun bounce");
|
|
65479
65761
|
}
|
|
65762
|
+
if (latestTask.paused) {
|
|
65763
|
+
executorLog.log(`${taskId}: workflow rerun deferred \u2014 task is paused`);
|
|
65764
|
+
return "deferred-paused";
|
|
65765
|
+
}
|
|
65480
65766
|
if (latestTask.column === "in-progress") {
|
|
65481
65767
|
const originalExecutionStartedAt = latestTask.executionStartedAt;
|
|
65482
65768
|
if (preserveResumeState) {
|
|
@@ -65488,11 +65774,21 @@ The tool prevents your session from being killed by the inactivity watchdog duri
|
|
|
65488
65774
|
worktree: worktreePath,
|
|
65489
65775
|
executionStartedAt: originalExecutionStartedAt ?? null
|
|
65490
65776
|
});
|
|
65777
|
+
const pauseLabelAfterTodo = await this.getExecutionPauseLabel();
|
|
65778
|
+
if (pauseLabelAfterTodo) {
|
|
65779
|
+
executorLog.log(`${taskId}: workflow rerun parked in todo \u2014 ${pauseLabelAfterTodo} became active during bounce`);
|
|
65780
|
+
return "deferred-paused";
|
|
65781
|
+
}
|
|
65491
65782
|
await this.store.moveTask(taskId, "in-progress");
|
|
65492
65783
|
return "bounced";
|
|
65493
65784
|
}
|
|
65494
65785
|
if (latestTask.column === "todo") {
|
|
65495
65786
|
await this.store.updateTask(taskId, { worktree: worktreePath });
|
|
65787
|
+
const pauseLabelBeforeResume = await this.getExecutionPauseLabel();
|
|
65788
|
+
if (pauseLabelBeforeResume) {
|
|
65789
|
+
executorLog.log(`${taskId}: workflow rerun parked in todo \u2014 ${pauseLabelBeforeResume} became active before resume`);
|
|
65790
|
+
return "deferred-paused";
|
|
65791
|
+
}
|
|
65496
65792
|
await this.store.moveTask(taskId, "in-progress");
|
|
65497
65793
|
return "bounced";
|
|
65498
65794
|
}
|
|
@@ -65508,8 +65804,10 @@ The tool prevents your session from being killed by the inactivity watchdog duri
|
|
|
65508
65804
|
const outcome = await this.performWorkflowRerunBounce(taskId, worktreePath, preserveResumeState);
|
|
65509
65805
|
if (outcome === "bounced") {
|
|
65510
65806
|
executorLog.log(successMessage);
|
|
65511
|
-
} else {
|
|
65807
|
+
} else if (outcome === "skipped-pending") {
|
|
65512
65808
|
executorLog.warn(`${taskId}: rerun bounce skipped \u2014 another bounce already in flight`);
|
|
65809
|
+
} else {
|
|
65810
|
+
executorLog.log(`${taskId}: rerun bounce deferred while pause is active`);
|
|
65513
65811
|
}
|
|
65514
65812
|
} catch (err) {
|
|
65515
65813
|
const errorMessage = err instanceof Error ? err.message : String(err);
|
|
@@ -65518,6 +65816,11 @@ The tool prevents your session from being killed by the inactivity watchdog duri
|
|
|
65518
65816
|
}, 0);
|
|
65519
65817
|
const watchdog = setTimeout(async () => {
|
|
65520
65818
|
this.workflowRerunWatchdogs.delete(taskId);
|
|
65819
|
+
const pauseLabel = await this.getExecutionPauseLabel();
|
|
65820
|
+
if (pauseLabel) {
|
|
65821
|
+
executorLog.log(`${taskId}: workflow rerun watchdog skipped \u2014 ${pauseLabel} active`);
|
|
65822
|
+
return;
|
|
65823
|
+
}
|
|
65521
65824
|
let currentTask = null;
|
|
65522
65825
|
try {
|
|
65523
65826
|
currentTask = await this.store.getTask(taskId);
|
|
@@ -65540,7 +65843,7 @@ The tool prevents your session from being killed by the inactivity watchdog duri
|
|
|
65540
65843
|
const outcome = await this.performWorkflowRerunBounce(taskId, worktreePath, preserveResumeState);
|
|
65541
65844
|
if (outcome === "bounced") {
|
|
65542
65845
|
executorLog.warn(`${taskId}: workflow rerun watchdog retry succeeded`);
|
|
65543
|
-
} else {
|
|
65846
|
+
} else if (outcome === "skipped-pending") {
|
|
65544
65847
|
executorLog.error(
|
|
65545
65848
|
`${taskId}: workflow rerun watchdog retry skipped \u2014 original bounce still in flight after ${WORKFLOW_RERUN_WATCHDOG_MS / 1e3}s; task may be stuck`
|
|
65546
65849
|
);
|
|
@@ -65548,6 +65851,8 @@ The tool prevents your session from being killed by the inactivity watchdog duri
|
|
|
65548
65851
|
taskId,
|
|
65549
65852
|
`Workflow rerun watchdog retry skipped \u2014 original bounce still in flight after ${WORKFLOW_RERUN_WATCHDOG_MS / 1e3}s; task may be stuck`
|
|
65550
65853
|
).catch(() => void 0);
|
|
65854
|
+
} else {
|
|
65855
|
+
executorLog.log(`${taskId}: workflow rerun watchdog retry deferred while pause is active`);
|
|
65551
65856
|
}
|
|
65552
65857
|
} catch (err) {
|
|
65553
65858
|
const errorMessage = err instanceof Error ? err.message : String(err);
|
|
@@ -65670,11 +65975,17 @@ The tool prevents your session from being killed by the inactivity watchdog duri
|
|
|
65670
65975
|
*/
|
|
65671
65976
|
async recoverCompletedTask(task) {
|
|
65672
65977
|
try {
|
|
65673
|
-
if (this.executing.has(task.id) || this.activeSessions.has(task.id) || this.activeStepExecutors.has(task.id) || this.resumingUnpaused.has(task.id)) {
|
|
65978
|
+
if (this.executing.has(task.id) || this.activeSessions.has(task.id) || this.activeStepExecutors.has(task.id) || this.activeWorkflowStepSessions.has(task.id) || this.resumingUnpaused.has(task.id)) {
|
|
65674
65979
|
executorLog.log(`${task.id}: skipping recoverCompletedTask \u2014 task has active execution in flight`);
|
|
65675
65980
|
return false;
|
|
65676
65981
|
}
|
|
65677
65982
|
const settings = await this.store.getSettings();
|
|
65983
|
+
if (settings.globalPause || settings.enginePaused) {
|
|
65984
|
+
executorLog.log(
|
|
65985
|
+
`${task.id}: skipping recoverCompletedTask \u2014 ${settings.globalPause ? "global pause" : "engine pause"} active`
|
|
65986
|
+
);
|
|
65987
|
+
return false;
|
|
65988
|
+
}
|
|
65678
65989
|
if (task.worktree && existsSync27(task.worktree)) {
|
|
65679
65990
|
const modifiedFiles = await this.captureModifiedFiles(task.worktree, task.baseCommitSha);
|
|
65680
65991
|
if (modifiedFiles.length > 0) {
|
|
@@ -65682,7 +65993,16 @@ The tool prevents your session from being killed by the inactivity watchdog duri
|
|
|
65682
65993
|
executorLog.log(`${task.id}: recovered ${modifiedFiles.length} modified files`);
|
|
65683
65994
|
}
|
|
65684
65995
|
if (task.executionMode !== "fast") {
|
|
65996
|
+
if (await this.shouldDeferCompletionForGlobalPause(task.id, "before workflow steps during completed-task recovery")) {
|
|
65997
|
+
return false;
|
|
65998
|
+
}
|
|
65685
65999
|
const workflowResult = await this.runWorkflowSteps(task, task.worktree, settings);
|
|
66000
|
+
if (workflowResult === "deferred-paused") {
|
|
66001
|
+
if (this.pausedAborted.has(task.id)) {
|
|
66002
|
+
this.pausedAborted.delete(task.id);
|
|
66003
|
+
}
|
|
66004
|
+
return false;
|
|
66005
|
+
}
|
|
65686
66006
|
if (!workflowResult.allPassed) {
|
|
65687
66007
|
await this.sendTaskBackForFix(task, task.worktree, workflowResult.feedback, workflowResult.stepName || "Unknown", "Workflow step failed during recovery", false);
|
|
65688
66008
|
return true;
|
|
@@ -65691,6 +66011,9 @@ The tool prevents your session from being killed by the inactivity watchdog duri
|
|
|
65691
66011
|
executorLog.log(`${task.id}: fast mode \u2014 skipping workflow steps on auto-recovery`);
|
|
65692
66012
|
}
|
|
65693
66013
|
}
|
|
66014
|
+
if (await this.shouldDeferCompletionForGlobalPause(task.id, "before in-review transition during completed-task recovery")) {
|
|
66015
|
+
return false;
|
|
66016
|
+
}
|
|
65694
66017
|
await this.persistTokenUsage(task.id);
|
|
65695
66018
|
await this.store.moveTask(task.id, "in-review");
|
|
65696
66019
|
this.clearCompletedTaskWatchdog(task.id);
|
|
@@ -65756,6 +66079,13 @@ The tool prevents your session from being killed by the inactivity watchdog duri
|
|
|
65756
66079
|
* directly to in-review without spawning a new agent session.
|
|
65757
66080
|
*/
|
|
65758
66081
|
async resumeOrphaned() {
|
|
66082
|
+
const settings = await this.store.getSettings();
|
|
66083
|
+
if (settings.globalPause || settings.enginePaused) {
|
|
66084
|
+
executorLog.log(
|
|
66085
|
+
`resumeOrphaned skipped \u2014 ${settings.globalPause ? "global pause" : "engine pause"} is active`
|
|
66086
|
+
);
|
|
66087
|
+
return;
|
|
66088
|
+
}
|
|
65759
66089
|
const tasks = await this.store.listTasks({ slim: true, column: "in-progress" });
|
|
65760
66090
|
const inProgress = tasks.filter(
|
|
65761
66091
|
(t) => t.column === "in-progress" && !this.executing.has(t.id) && !t.paused
|
|
@@ -66194,8 +66524,21 @@ The tool prevents your session from being killed by the inactivity watchdog duri
|
|
|
66194
66524
|
await audit.filesystem({ type: "file:capture-modified", target: task.id, metadata: { files: modifiedFiles } });
|
|
66195
66525
|
}
|
|
66196
66526
|
this.scheduleCompletedTaskWatchdog(task.id, "step-session completion");
|
|
66527
|
+
if (await this.shouldDeferCompletionForGlobalPause(task.id, "before workflow steps after step-session completion")) {
|
|
66528
|
+
return;
|
|
66529
|
+
}
|
|
66197
66530
|
if (executionMode !== "fast") {
|
|
66198
66531
|
const workflowResult = await this.runWorkflowSteps(task, worktreePath, settings);
|
|
66532
|
+
if (workflowResult === "deferred-paused") {
|
|
66533
|
+
if (await this.parkTaskAfterWorkflowStepPause(task.id)) {
|
|
66534
|
+
this.pausedAborted.delete(task.id);
|
|
66535
|
+
return;
|
|
66536
|
+
}
|
|
66537
|
+
if (this.pausedAborted.has(task.id)) {
|
|
66538
|
+
this.pausedAborted.delete(task.id);
|
|
66539
|
+
}
|
|
66540
|
+
return;
|
|
66541
|
+
}
|
|
66199
66542
|
if (!workflowResult.allPassed) {
|
|
66200
66543
|
if (workflowResult.revisionRequested) {
|
|
66201
66544
|
await this.handleWorkflowRevisionRequest(task, worktreePath, workflowResult.feedback, workflowResult.stepName);
|
|
@@ -66213,6 +66556,9 @@ The tool prevents your session from being killed by the inactivity watchdog duri
|
|
|
66213
66556
|
await this.store.logEntry(task.id, "Fast mode \u2014 pre-merge workflow steps skipped", void 0, this.currentRunContext);
|
|
66214
66557
|
}
|
|
66215
66558
|
await this.store.updateTask(task.id, { workflowStepRetries: void 0, taskDoneRetryCount: null });
|
|
66559
|
+
if (await this.shouldDeferCompletionForGlobalPause(task.id, "before in-review transition after step-session completion")) {
|
|
66560
|
+
return;
|
|
66561
|
+
}
|
|
66216
66562
|
await this.store.moveTask(task.id, "in-review");
|
|
66217
66563
|
this.clearCompletedTaskWatchdog(task.id);
|
|
66218
66564
|
await audit.database({ type: "task:move", target: task.id, metadata: { to: "in-review" } });
|
|
@@ -66565,6 +66911,9 @@ The tool prevents your session from being killed by the inactivity watchdog duri
|
|
|
66565
66911
|
this.pausedAborted.delete(task.id);
|
|
66566
66912
|
wasPaused = true;
|
|
66567
66913
|
if (await this.shouldFinalizeCompletedTask(task.id, taskDone)) {
|
|
66914
|
+
if (await this.shouldDeferCompletionForGlobalPause(task.id, "paused after completion")) {
|
|
66915
|
+
return;
|
|
66916
|
+
}
|
|
66568
66917
|
executorLog.log(`${task.id} paused after completion (graceful session exit) \u2014 finalizing to in-review`);
|
|
66569
66918
|
await this.store.logEntry(task.id, "Execution paused after completion \u2014 finalizing to in-review");
|
|
66570
66919
|
await this.persistTokenUsage(task.id);
|
|
@@ -66601,8 +66950,23 @@ The tool prevents your session from being killed by the inactivity watchdog duri
|
|
|
66601
66950
|
executorLog.log(`${task.id}: captured ${modifiedFiles.length} modified files`);
|
|
66602
66951
|
}
|
|
66603
66952
|
this.scheduleCompletedTaskWatchdog(task.id, "task completion");
|
|
66953
|
+
if (await this.shouldDeferCompletionForGlobalPause(task.id, "before workflow steps after task completion")) {
|
|
66954
|
+
return;
|
|
66955
|
+
}
|
|
66604
66956
|
if (executionMode !== "fast") {
|
|
66605
66957
|
const workflowResult = await this.runWorkflowSteps(task, worktreePath, settings);
|
|
66958
|
+
if (workflowResult === "deferred-paused") {
|
|
66959
|
+
if (await this.parkTaskAfterWorkflowStepPause(task.id)) {
|
|
66960
|
+
this.pausedAborted.delete(task.id);
|
|
66961
|
+
wasPaused = true;
|
|
66962
|
+
return;
|
|
66963
|
+
}
|
|
66964
|
+
if (this.pausedAborted.has(task.id)) {
|
|
66965
|
+
this.pausedAborted.delete(task.id);
|
|
66966
|
+
wasPaused = true;
|
|
66967
|
+
}
|
|
66968
|
+
return;
|
|
66969
|
+
}
|
|
66606
66970
|
if (!workflowResult.allPassed) {
|
|
66607
66971
|
if (workflowResult.revisionRequested) {
|
|
66608
66972
|
await this.handleWorkflowRevisionRequest(task, worktreePath, workflowResult.feedback, workflowResult.stepName);
|
|
@@ -66620,6 +66984,9 @@ The tool prevents your session from being killed by the inactivity watchdog duri
|
|
|
66620
66984
|
await this.store.logEntry(task.id, "Fast mode \u2014 pre-merge workflow steps skipped", void 0, this.currentRunContext);
|
|
66621
66985
|
}
|
|
66622
66986
|
await this.store.updateTask(task.id, { workflowStepRetries: void 0, taskDoneRetryCount: null });
|
|
66987
|
+
if (await this.shouldDeferCompletionForGlobalPause(task.id, "before in-review transition after task completion")) {
|
|
66988
|
+
return;
|
|
66989
|
+
}
|
|
66623
66990
|
await this.persistTokenUsage(task.id);
|
|
66624
66991
|
await this.store.moveTask(task.id, "in-review");
|
|
66625
66992
|
this.clearCompletedTaskWatchdog(task.id);
|
|
@@ -66744,8 +67111,23 @@ The tool prevents your session from being killed by the inactivity watchdog duri
|
|
|
66744
67111
|
executorLog.log(`${task.id}: captured ${modifiedFiles.length} modified files`);
|
|
66745
67112
|
}
|
|
66746
67113
|
this.scheduleCompletedTaskWatchdog(task.id, "task completion retry");
|
|
67114
|
+
if (await this.shouldDeferCompletionForGlobalPause(task.id, "before workflow steps after task completion retry")) {
|
|
67115
|
+
return;
|
|
67116
|
+
}
|
|
66747
67117
|
if (executionMode !== "fast") {
|
|
66748
67118
|
const workflowResult = await this.runWorkflowSteps(task, worktreePath, settings);
|
|
67119
|
+
if (workflowResult === "deferred-paused") {
|
|
67120
|
+
if (await this.parkTaskAfterWorkflowStepPause(task.id)) {
|
|
67121
|
+
this.pausedAborted.delete(task.id);
|
|
67122
|
+
wasPaused = true;
|
|
67123
|
+
return;
|
|
67124
|
+
}
|
|
67125
|
+
if (this.pausedAborted.has(task.id)) {
|
|
67126
|
+
this.pausedAborted.delete(task.id);
|
|
67127
|
+
wasPaused = true;
|
|
67128
|
+
}
|
|
67129
|
+
return;
|
|
67130
|
+
}
|
|
66749
67131
|
if (!workflowResult.allPassed) {
|
|
66750
67132
|
if (workflowResult.revisionRequested) {
|
|
66751
67133
|
await this.handleWorkflowRevisionRequest(task, worktreePath, workflowResult.feedback, workflowResult.stepName);
|
|
@@ -66759,6 +67141,9 @@ The tool prevents your session from being killed by the inactivity watchdog duri
|
|
|
66759
67141
|
await this.store.logEntry(task.id, "Fast mode \u2014 pre-merge workflow steps skipped", void 0, this.currentRunContext);
|
|
66760
67142
|
}
|
|
66761
67143
|
await this.store.updateTask(task.id, { workflowStepRetries: void 0, taskDoneRetryCount: null });
|
|
67144
|
+
if (await this.shouldDeferCompletionForGlobalPause(task.id, "before in-review transition after task completion retry")) {
|
|
67145
|
+
return;
|
|
67146
|
+
}
|
|
66762
67147
|
await this.persistTokenUsage(task.id);
|
|
66763
67148
|
await this.store.moveTask(task.id, "in-review");
|
|
66764
67149
|
this.clearCompletedTaskWatchdog(task.id);
|
|
@@ -66851,6 +67236,9 @@ The tool prevents your session from being killed by the inactivity watchdog duri
|
|
|
66851
67236
|
} else if (this.pausedAborted.has(task.id)) {
|
|
66852
67237
|
this.pausedAborted.delete(task.id);
|
|
66853
67238
|
if (await this.shouldFinalizeCompletedTask(task.id, taskDone)) {
|
|
67239
|
+
if (await this.shouldDeferCompletionForGlobalPause(task.id, "paused after completion")) {
|
|
67240
|
+
return;
|
|
67241
|
+
}
|
|
66854
67242
|
executorLog.log(`${task.id} paused after completion \u2014 finalizing to in-review`);
|
|
66855
67243
|
await this.store.logEntry(task.id, "Execution paused after completion \u2014 finalizing to in-review", void 0, this.currentRunContext);
|
|
66856
67244
|
await this.persistTokenUsage(task.id);
|
|
@@ -67209,22 +67597,28 @@ The tool prevents your session from being killed by the inactivity watchdog duri
|
|
|
67209
67597
|
if (params.summary) {
|
|
67210
67598
|
await store.updateTask(taskId, { summary: params.summary });
|
|
67211
67599
|
}
|
|
67212
|
-
await store.
|
|
67600
|
+
const settings = await store.getSettings();
|
|
67601
|
+
const hardPauseActive = Boolean(task.paused || settings.globalPause);
|
|
67602
|
+
if (hardPauseActive) {
|
|
67603
|
+
await store.updateTask(taskId, { status: null });
|
|
67604
|
+
} else {
|
|
67605
|
+
await store.updateTask(taskId, { paused: false, status: null });
|
|
67606
|
+
}
|
|
67213
67607
|
await store.logEntry(taskId, "Task marked done by agent");
|
|
67214
67608
|
const latestTask = await store.getTask(taskId);
|
|
67215
67609
|
let latestColumn = latestTask.column;
|
|
67216
67610
|
if (latestColumn === "todo") {
|
|
67217
67611
|
await store.logEntry(
|
|
67218
67612
|
taskId,
|
|
67219
|
-
"fn_task_done called while task was in todo \u2014 promoting to in-progress before completion handoff"
|
|
67613
|
+
hardPauseActive ? "fn_task_done called while task was in todo during pause \u2014 promoting to in-progress for deferred completion handoff" : "fn_task_done called while task was in todo \u2014 promoting to in-progress before completion handoff"
|
|
67220
67614
|
);
|
|
67221
67615
|
await store.moveTask(taskId, "in-progress");
|
|
67222
67616
|
latestColumn = "in-progress";
|
|
67223
67617
|
}
|
|
67224
|
-
if (latestColumn === "in-progress") {
|
|
67618
|
+
if (latestColumn === "in-progress" && !hardPauseActive) {
|
|
67225
67619
|
this.scheduleCompletedTaskWatchdog(taskId, "fn_task_done");
|
|
67226
67620
|
}
|
|
67227
|
-
const successMessage = params.summary ? "Task marked complete with summary. All steps done. Moving to in-review." : "Task marked complete. All steps done. Moving to in-review.";
|
|
67621
|
+
const successMessage = hardPauseActive ? "Task marked complete. Completion handoff deferred until pause is cleared." : params.summary ? "Task marked complete with summary. All steps done. Moving to in-review." : "Task marked complete. All steps done. Moving to in-review.";
|
|
67228
67622
|
return {
|
|
67229
67623
|
content: [{ type: "text", text: successMessage }],
|
|
67230
67624
|
details: {}
|
|
@@ -67799,6 +68193,9 @@ ${failureFeedback}
|
|
|
67799
68193
|
await this.store.updateTask(task.id, { workflowStepResults: results });
|
|
67800
68194
|
continue;
|
|
67801
68195
|
}
|
|
68196
|
+
if (await this.shouldDeferWorkflowStepCompletion(task.id, `before workflow step '${ws.name}'`)) {
|
|
68197
|
+
return "deferred-paused";
|
|
68198
|
+
}
|
|
67802
68199
|
await this.store.logEntry(task.id, `[pre-merge] Starting workflow step: ${ws.name} (${stepMode} mode)`);
|
|
67803
68200
|
executorLog.log(`${task.id} \u2014 [pre-merge] running workflow step: ${ws.name} (${stepMode} mode)`);
|
|
67804
68201
|
const startedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -67813,6 +68210,9 @@ ${failureFeedback}
|
|
|
67813
68210
|
await this.store.updateTask(task.id, { workflowStepResults: results });
|
|
67814
68211
|
try {
|
|
67815
68212
|
const result = stepMode === "script" ? await this.executeScriptWorkflowStep(task, ws, worktreePath, settings) : await this.executeWorkflowStep(task, ws, worktreePath, settings);
|
|
68213
|
+
if (await this.shouldDeferWorkflowStepCompletion(task.id, `workflow step '${ws.name}'`)) {
|
|
68214
|
+
return "deferred-paused";
|
|
68215
|
+
}
|
|
67816
68216
|
const completedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
67817
68217
|
if (result.success) {
|
|
67818
68218
|
await this.store.logEntry(task.id, `[timing] Workflow step '${ws.name}' completed in ${Date.now() - stepStartedAtMs}ms`);
|
|
@@ -67878,6 +68278,9 @@ ${failureFeedback}
|
|
|
67878
68278
|
};
|
|
67879
68279
|
}
|
|
67880
68280
|
} catch (err) {
|
|
68281
|
+
if (await this.shouldDeferWorkflowStepCompletion(task.id, `workflow step '${ws.name}'`)) {
|
|
68282
|
+
return "deferred-paused";
|
|
68283
|
+
}
|
|
67881
68284
|
const { message: errorMessage, detail: errorDetail, stack: errorStack } = formatError(err);
|
|
67882
68285
|
const completedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
67883
68286
|
await this.store.logEntry(
|
|
@@ -68043,6 +68446,7 @@ and show an appropriate message to the user.\`
|
|
|
68043
68446
|
task.id,
|
|
68044
68447
|
`Workflow step '${workflowStep.name}' using model: ${describeModel(session)}${useOverride && attemptLabel === "primary" ? " (workflow step override)" : ""}${attemptLabel === "fallback" ? " (fallback after timeout)" : ""}`
|
|
68045
68448
|
);
|
|
68449
|
+
this.activeWorkflowStepSessions.set(task.id, session);
|
|
68046
68450
|
let output = "";
|
|
68047
68451
|
session.subscribe((event) => {
|
|
68048
68452
|
if (event.type === "message_update") {
|
|
@@ -68115,6 +68519,10 @@ Review the work done in this worktree and evaluate it against the criteria in yo
|
|
|
68115
68519
|
return { success: false, error: errorMessage };
|
|
68116
68520
|
} finally {
|
|
68117
68521
|
if (timeoutHandle) clearTimeout(timeoutHandle);
|
|
68522
|
+
const activeWorkflowStepSession = this.activeWorkflowStepSessions.get(task.id);
|
|
68523
|
+
if (activeWorkflowStepSession === session) {
|
|
68524
|
+
this.activeWorkflowStepSessions.delete(task.id);
|
|
68525
|
+
}
|
|
68118
68526
|
void timedOut;
|
|
68119
68527
|
}
|
|
68120
68528
|
};
|
|
@@ -69858,6 +70266,15 @@ var init_scheduler = __esm({
|
|
|
69858
70266
|
schedulerLog.log(`Task ${task.id} is paused \u2014 skipping dispatch`);
|
|
69859
70267
|
continue;
|
|
69860
70268
|
}
|
|
70269
|
+
const latestSettings = await this.store.getSettings();
|
|
70270
|
+
if (latestSettings.globalPause) {
|
|
70271
|
+
schedulerLog.log(`Task ${task.id} dispatch aborted \u2014 globalPause became active mid-pass`);
|
|
70272
|
+
continue;
|
|
70273
|
+
}
|
|
70274
|
+
if (latestSettings.enginePaused) {
|
|
70275
|
+
schedulerLog.log(`Task ${task.id} dispatch aborted \u2014 enginePaused became active mid-pass`);
|
|
70276
|
+
continue;
|
|
70277
|
+
}
|
|
69861
70278
|
let effectiveNode = resolveEffectiveNode(freshTask, settings);
|
|
69862
70279
|
schedulerLog.log(`Task ${task.id} routed to node=${effectiveNode.nodeId ?? "local"} (source=${effectiveNode.source})`);
|
|
69863
70280
|
if (effectiveNode.nodeId !== void 0 && this.options.nodeHealthMonitor) {
|
|
@@ -74995,6 +75412,36 @@ function execCommand(command, options) {
|
|
|
74995
75412
|
});
|
|
74996
75413
|
});
|
|
74997
75414
|
}
|
|
75415
|
+
function isInProcessBackupCommand(command) {
|
|
75416
|
+
if (!command) return false;
|
|
75417
|
+
const trimmed = command.trim();
|
|
75418
|
+
if (!trimmed) return false;
|
|
75419
|
+
if (SHELL_METACHARACTERS_REGEX.test(trimmed)) return false;
|
|
75420
|
+
const tokens = trimmed.split(/\s+/).map((tok) => tok.toLowerCase());
|
|
75421
|
+
let cursor = 0;
|
|
75422
|
+
if (tokens[cursor] === "npx") {
|
|
75423
|
+
cursor += 1;
|
|
75424
|
+
while (cursor < tokens.length) {
|
|
75425
|
+
const tok = tokens[cursor];
|
|
75426
|
+
if (tok === void 0 || !tok.startsWith("-")) break;
|
|
75427
|
+
const takesValue = (tok === "-p" || tok === "--package") && cursor + 1 < tokens.length && tokens[cursor + 1] !== void 0 && !tokens[cursor + 1].startsWith("-");
|
|
75428
|
+
cursor += takesValue ? 2 : 1;
|
|
75429
|
+
}
|
|
75430
|
+
}
|
|
75431
|
+
const binary = tokens[cursor];
|
|
75432
|
+
if (!binary || !FUSION_BINARY_TOKENS.has(binary)) return false;
|
|
75433
|
+
cursor += 1;
|
|
75434
|
+
if (tokens[cursor] !== "backup") return false;
|
|
75435
|
+
cursor += 1;
|
|
75436
|
+
if (tokens[cursor] !== "--create") return false;
|
|
75437
|
+
cursor += 1;
|
|
75438
|
+
for (; cursor < tokens.length; cursor += 1) {
|
|
75439
|
+
const tok = tokens[cursor];
|
|
75440
|
+
if (!tok) continue;
|
|
75441
|
+
if (!tok.startsWith("-")) return false;
|
|
75442
|
+
}
|
|
75443
|
+
return true;
|
|
75444
|
+
}
|
|
74998
75445
|
async function createAiPromptExecutor(cwd) {
|
|
74999
75446
|
const disposeLog = createLogger2("cron-runner");
|
|
75000
75447
|
return async (prompt, modelProvider, modelId) => {
|
|
@@ -75034,7 +75481,7 @@ function truncateOutput(stdout, stderr) {
|
|
|
75034
75481
|
}
|
|
75035
75482
|
return combined;
|
|
75036
75483
|
}
|
|
75037
|
-
var log14, DEFAULT_TIMEOUT_MS6, MAX_BUFFER, MAX_OUTPUT_LENGTH, DEFAULT_POLL_INTERVAL_MS, MIN_POLL_INTERVAL_MS, CronRunner, AI_AUTOMATION_SYSTEM_PROMPT;
|
|
75484
|
+
var log14, FUSION_BINARY_TOKENS, SHELL_METACHARACTERS_REGEX, DEFAULT_TIMEOUT_MS6, MAX_BUFFER, MAX_OUTPUT_LENGTH, DEFAULT_POLL_INTERVAL_MS, MIN_POLL_INTERVAL_MS, CronRunner, AI_AUTOMATION_SYSTEM_PROMPT;
|
|
75038
75485
|
var init_cron_runner = __esm({
|
|
75039
75486
|
"../engine/src/cron-runner.ts"() {
|
|
75040
75487
|
"use strict";
|
|
@@ -75043,6 +75490,14 @@ var init_cron_runner = __esm({
|
|
|
75043
75490
|
init_shell_utils();
|
|
75044
75491
|
init_pi();
|
|
75045
75492
|
log14 = createLogger2("cron-runner");
|
|
75493
|
+
FUSION_BINARY_TOKENS = /* @__PURE__ */ new Set([
|
|
75494
|
+
"fn",
|
|
75495
|
+
"fusion",
|
|
75496
|
+
"runfusion",
|
|
75497
|
+
"runfusion.ai",
|
|
75498
|
+
"@runfusion/fusion"
|
|
75499
|
+
]);
|
|
75500
|
+
SHELL_METACHARACTERS_REGEX = /[&|;<>`$()]/;
|
|
75046
75501
|
DEFAULT_TIMEOUT_MS6 = 5 * 60 * 1e3;
|
|
75047
75502
|
MAX_BUFFER = 1024 * 1024;
|
|
75048
75503
|
MAX_OUTPUT_LENGTH = 10 * 1024;
|
|
@@ -75183,6 +75638,9 @@ var init_cron_runner = __esm({
|
|
|
75183
75638
|
*/
|
|
75184
75639
|
async executeLegacyCommand(schedule, startedAt) {
|
|
75185
75640
|
log14.log(`Executing ${schedule.name} (${schedule.id}): ${schedule.command}`);
|
|
75641
|
+
if (isInProcessBackupCommand(schedule.command)) {
|
|
75642
|
+
return this.executeBackupInProcess(schedule, startedAt);
|
|
75643
|
+
}
|
|
75186
75644
|
try {
|
|
75187
75645
|
const timeoutMs = schedule.timeoutMs ?? DEFAULT_TIMEOUT_MS6;
|
|
75188
75646
|
const { stdout, stderr } = await execCommand(schedule.command, {
|
|
@@ -75213,6 +75671,47 @@ var init_cron_runner = __esm({
|
|
|
75213
75671
|
};
|
|
75214
75672
|
}
|
|
75215
75673
|
}
|
|
75674
|
+
/**
|
|
75675
|
+
* Run an auto-backup schedule in-process via the engine's open TaskStore,
|
|
75676
|
+
* bypassing the shell-out that would otherwise invoke an outdated fusion
|
|
75677
|
+
* binary on PATH. See `isInProcessBackupCommand` for the matching contract.
|
|
75678
|
+
*/
|
|
75679
|
+
async executeBackupInProcess(schedule, startedAt) {
|
|
75680
|
+
const action = await this.runBackupActionInProcess();
|
|
75681
|
+
if (action.success) {
|
|
75682
|
+
log14.log(`\u2713 ${schedule.name} completed in-process`);
|
|
75683
|
+
} else {
|
|
75684
|
+
log14.warn(`\u2717 ${schedule.name} in-process backup ${action.error ? `threw: ${action.error}` : `reported failure: ${action.output}`}`);
|
|
75685
|
+
}
|
|
75686
|
+
return {
|
|
75687
|
+
success: action.success,
|
|
75688
|
+
output: action.output,
|
|
75689
|
+
error: action.error,
|
|
75690
|
+
startedAt,
|
|
75691
|
+
completedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
75692
|
+
};
|
|
75693
|
+
}
|
|
75694
|
+
/**
|
|
75695
|
+
* Shared in-process backup execution used by both the legacy-command path
|
|
75696
|
+
* and the command-step path. Returns the success/output/error tuple in
|
|
75697
|
+
* a shape that callers can wrap into either a run or a step result.
|
|
75698
|
+
*/
|
|
75699
|
+
async runBackupActionInProcess() {
|
|
75700
|
+
try {
|
|
75701
|
+
const { runBackupCommand: runBackupCommand2 } = await Promise.resolve().then(() => (init_src(), src_exports));
|
|
75702
|
+
const fusionDir = this.store.getFusionDir();
|
|
75703
|
+
const settings = await this.store.getSettings();
|
|
75704
|
+
const result = await runBackupCommand2(fusionDir, settings);
|
|
75705
|
+
return {
|
|
75706
|
+
success: result.success,
|
|
75707
|
+
output: truncateOutput(result.output ?? "", ""),
|
|
75708
|
+
error: result.success ? void 0 : result.output
|
|
75709
|
+
};
|
|
75710
|
+
} catch (err) {
|
|
75711
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
75712
|
+
return { success: false, output: "", error: message };
|
|
75713
|
+
}
|
|
75714
|
+
}
|
|
75216
75715
|
/**
|
|
75217
75716
|
* Execute multiple steps sequentially.
|
|
75218
75717
|
* Aggregates per-step results into an overall AutomationRunResult.
|
|
@@ -75301,6 +75800,19 @@ var init_cron_runner = __esm({
|
|
|
75301
75800
|
completedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
75302
75801
|
};
|
|
75303
75802
|
}
|
|
75803
|
+
if (isInProcessBackupCommand(step.command)) {
|
|
75804
|
+
const action = await this.runBackupActionInProcess();
|
|
75805
|
+
return {
|
|
75806
|
+
stepId: step.id,
|
|
75807
|
+
stepName: step.name,
|
|
75808
|
+
stepIndex,
|
|
75809
|
+
success: action.success,
|
|
75810
|
+
output: action.output,
|
|
75811
|
+
error: action.error,
|
|
75812
|
+
startedAt,
|
|
75813
|
+
completedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
75814
|
+
};
|
|
75815
|
+
}
|
|
75304
75816
|
try {
|
|
75305
75817
|
const { stdout, stderr } = await execCommand(step.command, {
|
|
75306
75818
|
timeout: timeoutMs,
|
|
@@ -75492,6 +76004,7 @@ var init_routine_runner = __esm({
|
|
|
75492
76004
|
"../engine/src/routine-runner.ts"() {
|
|
75493
76005
|
"use strict";
|
|
75494
76006
|
import_cron_parser4 = __toESM(require_dist2(), 1);
|
|
76007
|
+
init_cron_runner();
|
|
75495
76008
|
init_logger2();
|
|
75496
76009
|
init_shell_utils();
|
|
75497
76010
|
log15 = createLogger2("routine-runner");
|
|
@@ -75649,6 +76162,30 @@ var init_routine_runner = __esm({
|
|
|
75649
76162
|
return this.executeCommand(routine.command ?? "", routine.timeoutMs, startedAt);
|
|
75650
76163
|
}
|
|
75651
76164
|
async executeCommand(command, timeoutMs, startedAt) {
|
|
76165
|
+
if (isInProcessBackupCommand(command) && this.options.taskStore) {
|
|
76166
|
+
try {
|
|
76167
|
+
const { runBackupCommand: runBackupCommand2 } = await Promise.resolve().then(() => (init_src(), src_exports));
|
|
76168
|
+
const fusionDir = this.options.taskStore.getFusionDir();
|
|
76169
|
+
const settings = await this.options.taskStore.getSettings();
|
|
76170
|
+
const result = await runBackupCommand2(fusionDir, settings);
|
|
76171
|
+
return {
|
|
76172
|
+
success: result.success,
|
|
76173
|
+
output: truncateOutput2(result.output ?? "", ""),
|
|
76174
|
+
error: result.success ? void 0 : result.output,
|
|
76175
|
+
startedAt,
|
|
76176
|
+
completedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
76177
|
+
};
|
|
76178
|
+
} catch (err) {
|
|
76179
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
76180
|
+
return {
|
|
76181
|
+
success: false,
|
|
76182
|
+
output: "",
|
|
76183
|
+
error: message,
|
|
76184
|
+
startedAt,
|
|
76185
|
+
completedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
76186
|
+
};
|
|
76187
|
+
}
|
|
76188
|
+
}
|
|
75652
76189
|
try {
|
|
75653
76190
|
const { stdout, stderr } = await execAsync6(command, {
|
|
75654
76191
|
timeout: timeoutMs ?? DEFAULT_TIMEOUT_MS7,
|
|
@@ -76440,6 +76977,13 @@ var init_self_healing = __esm({
|
|
|
76440
76977
|
* stale in-progress/planning tasks that no longer have a live worker.
|
|
76441
76978
|
*/
|
|
76442
76979
|
async runStartupRecovery() {
|
|
76980
|
+
const settings = await this.store.getSettings();
|
|
76981
|
+
if (settings.globalPause || settings.enginePaused) {
|
|
76982
|
+
log16.log(
|
|
76983
|
+
`Startup recovery skipped \u2014 ${settings.globalPause ? "global pause" : "engine pause"} is active`
|
|
76984
|
+
);
|
|
76985
|
+
return;
|
|
76986
|
+
}
|
|
76443
76987
|
const steps = [
|
|
76444
76988
|
{ name: "no-progress-no-task-done", fn: () => this.recoverNoProgressNoTaskDoneFailures().then(() => void 0) },
|
|
76445
76989
|
{ name: "completed-tasks", fn: () => this.recoverCompletedTasks().then(() => void 0) },
|
|
@@ -76790,27 +77334,34 @@ var init_self_healing = __esm({
|
|
|
76790
77334
|
log16.error(`Maintenance batch 1 step "${fn.name}" failed: ${stepErr instanceof Error ? stepErr.message : String(stepErr)}`);
|
|
76791
77335
|
}
|
|
76792
77336
|
}
|
|
76793
|
-
const
|
|
76794
|
-
|
|
76795
|
-
|
|
76796
|
-
|
|
76797
|
-
|
|
76798
|
-
|
|
76799
|
-
|
|
76800
|
-
|
|
76801
|
-
|
|
76802
|
-
|
|
76803
|
-
|
|
76804
|
-
|
|
76805
|
-
|
|
76806
|
-
|
|
76807
|
-
|
|
76808
|
-
|
|
76809
|
-
|
|
76810
|
-
|
|
76811
|
-
|
|
76812
|
-
|
|
76813
|
-
|
|
77337
|
+
const recoverySettings = await this.store.getSettings();
|
|
77338
|
+
if (recoverySettings.globalPause || recoverySettings.enginePaused) {
|
|
77339
|
+
log16.log(
|
|
77340
|
+
`Maintenance batch 2 skipped \u2014 ${recoverySettings.globalPause ? "global pause" : "engine pause"} is active`
|
|
77341
|
+
);
|
|
77342
|
+
} else {
|
|
77343
|
+
const batch2Fns = [
|
|
77344
|
+
{ name: "recover-completed-tasks", fn: () => this.recoverCompletedTasks() },
|
|
77345
|
+
{ name: "recover-stale-incomplete-review", fn: () => this.recoverStaleIncompleteReviewTasks() },
|
|
77346
|
+
{ name: "recover-failed-pre-merge-steps", fn: () => this.recoverReviewTasksWithFailedPreMergeSteps() },
|
|
77347
|
+
{ name: "recover-interrupted-merging", fn: () => this.recoverInterruptedMergingTasks() },
|
|
77348
|
+
{ name: "recover-mergeable-review", fn: () => this.recoverMergeableReviewTasks() },
|
|
77349
|
+
{ name: "recover-merged-review", fn: () => this.recoverMergedReviewTasks() },
|
|
77350
|
+
{ name: "recover-misclassified-failures", fn: () => this.recoverMisclassifiedFailures() },
|
|
77351
|
+
{ name: "recover-no-progress-no-task-done", fn: () => this.recoverNoProgressNoTaskDoneFailures() },
|
|
77352
|
+
{ name: "recover-partial-progress-no-task-done", fn: () => this.recoverPartialProgressNoTaskDoneFailures() },
|
|
77353
|
+
{ name: "recover-orphaned-executions", fn: () => this.recoverOrphanedExecutions() },
|
|
77354
|
+
{ name: "recover-approved-triage", fn: () => this.recoverApprovedTriageTasks() },
|
|
77355
|
+
{ name: "recover-orphaned-planning", fn: () => this.recoverOrphanedPlanningTasks() },
|
|
77356
|
+
{ name: "recover-ghost-review", fn: () => this.recoverGhostReviewTasks() }
|
|
77357
|
+
];
|
|
77358
|
+
for (const fn of batch2Fns) {
|
|
77359
|
+
try {
|
|
77360
|
+
await fn.fn();
|
|
77361
|
+
log16.log(`Maintenance batch 2 step "${fn.name}" succeeded`);
|
|
77362
|
+
} catch (stepErr) {
|
|
77363
|
+
log16.error(`Maintenance batch 2 step "${fn.name}" failed: ${stepErr instanceof Error ? stepErr.message : String(stepErr)}`);
|
|
77364
|
+
}
|
|
76814
77365
|
}
|
|
76815
77366
|
}
|
|
76816
77367
|
const batch3Fns = [
|
|
@@ -78543,6 +79094,10 @@ var init_in_process_runtime = __esm({
|
|
|
78543
79094
|
* before `start()` via `setMergeEnqueuer`.
|
|
78544
79095
|
*/
|
|
78545
79096
|
mergeEnqueuer;
|
|
79097
|
+
/** Tracks whether startup recovery was intentionally deferred due to pause state. */
|
|
79098
|
+
startupRecoveryDeferred = false;
|
|
79099
|
+
/** Prevent duplicate unpause recovery dispatches from racing each other. */
|
|
79100
|
+
resumeAfterUnpauseRunning = false;
|
|
78546
79101
|
/**
|
|
78547
79102
|
* Start the runtime and initialize all subsystems.
|
|
78548
79103
|
*
|
|
@@ -78576,7 +79131,7 @@ var init_in_process_runtime = __esm({
|
|
|
78576
79131
|
runtimeLog.log(`TaskStore initialized for project ${this.config.projectId}`);
|
|
78577
79132
|
}
|
|
78578
79133
|
this.messageStore = new MessageStoreClass(this.taskStore.getDatabase());
|
|
78579
|
-
this.pluginStore = new PluginStoreClass(this.
|
|
79134
|
+
this.pluginStore = new PluginStoreClass(this.config.workingDirectory);
|
|
78580
79135
|
await this.pluginStore.init();
|
|
78581
79136
|
this.pluginLoader = new PluginLoaderClass({
|
|
78582
79137
|
pluginStore: this.pluginStore,
|
|
@@ -78968,11 +79523,16 @@ var init_in_process_runtime = __esm({
|
|
|
78968
79523
|
this.selfHealingManager.start();
|
|
78969
79524
|
this.stuckTaskDetector.start();
|
|
78970
79525
|
this.setupEventForwarding();
|
|
78971
|
-
await this.
|
|
78972
|
-
|
|
78973
|
-
|
|
78974
|
-
runtimeLog.
|
|
78975
|
-
|
|
79526
|
+
const startupSettings = await this.taskStore.getSettings();
|
|
79527
|
+
if (startupSettings.globalPause || startupSettings.enginePaused) {
|
|
79528
|
+
this.startupRecoveryDeferred = true;
|
|
79529
|
+
runtimeLog.log(
|
|
79530
|
+
`Startup recovery deferred \u2014 ${startupSettings.globalPause ? "global pause" : "engine pause"} is active`
|
|
79531
|
+
);
|
|
79532
|
+
} else {
|
|
79533
|
+
this.startupRecoveryDeferred = false;
|
|
79534
|
+
await this.resumeStartupRecoverySequence();
|
|
79535
|
+
}
|
|
78976
79536
|
this.scheduler.start();
|
|
78977
79537
|
this.triageProcessor?.start();
|
|
78978
79538
|
this.missionExecutionLoop = missionExecutionLoop;
|
|
@@ -79138,6 +79698,45 @@ var init_in_process_runtime = __esm({
|
|
|
79138
79698
|
setMergeEnqueuer(enqueueMerge) {
|
|
79139
79699
|
this.mergeEnqueuer = enqueueMerge;
|
|
79140
79700
|
}
|
|
79701
|
+
/**
|
|
79702
|
+
* Resume executor/self-healing activity after an unpause transition.
|
|
79703
|
+
*
|
|
79704
|
+
* When startup recovery had been deferred, this replays the original startup
|
|
79705
|
+
* ordering so orphan resume and self-healing cannot race each other.
|
|
79706
|
+
*/
|
|
79707
|
+
async resumeAfterUnpause() {
|
|
79708
|
+
if (!this.taskStore || !this.executor || !this.selfHealingManager) {
|
|
79709
|
+
return;
|
|
79710
|
+
}
|
|
79711
|
+
if (this.resumeAfterUnpauseRunning) {
|
|
79712
|
+
return;
|
|
79713
|
+
}
|
|
79714
|
+
this.resumeAfterUnpauseRunning = true;
|
|
79715
|
+
try {
|
|
79716
|
+
const settings = await this.taskStore.getSettings();
|
|
79717
|
+
if (settings.globalPause || settings.enginePaused) {
|
|
79718
|
+
runtimeLog.log(
|
|
79719
|
+
`Unpause recovery still blocked \u2014 ${settings.globalPause ? "global pause" : "engine pause"} remains active`
|
|
79720
|
+
);
|
|
79721
|
+
return;
|
|
79722
|
+
}
|
|
79723
|
+
if (this.startupRecoveryDeferred) {
|
|
79724
|
+
await this.resumeStartupRecoverySequence();
|
|
79725
|
+
this.startupRecoveryDeferred = false;
|
|
79726
|
+
return;
|
|
79727
|
+
}
|
|
79728
|
+
await this.executor.resumeOrphaned();
|
|
79729
|
+
} finally {
|
|
79730
|
+
this.resumeAfterUnpauseRunning = false;
|
|
79731
|
+
}
|
|
79732
|
+
}
|
|
79733
|
+
async resumeStartupRecoverySequence() {
|
|
79734
|
+
await this.selfHealingManager.recoverNoProgressNoTaskDoneFailures();
|
|
79735
|
+
await this.executor.resumeOrphaned();
|
|
79736
|
+
void this.selfHealingManager.runStartupRecovery().catch((err) => {
|
|
79737
|
+
runtimeLog.error("Self-healing startup recovery failed:", err);
|
|
79738
|
+
});
|
|
79739
|
+
}
|
|
79141
79740
|
/**
|
|
79142
79741
|
* Get the project's TaskStore instance.
|
|
79143
79742
|
* @throws Error if runtime has not been started
|
|
@@ -82977,13 +83576,13 @@ ${detail}`
|
|
|
82977
83576
|
if (prev.globalPause && !s.globalPause) {
|
|
82978
83577
|
runtimeLog.log("Global unpause \u2014 resuming agentic activity");
|
|
82979
83578
|
try {
|
|
82980
|
-
const
|
|
82981
|
-
|
|
82982
|
-
(err) => runtimeLog.error("Failed to resume
|
|
83579
|
+
const runtime = this.runtime;
|
|
83580
|
+
runtime.resumeAfterUnpause?.().catch(
|
|
83581
|
+
(err) => runtimeLog.error("Failed to resume agentic activity on unpause:", err)
|
|
82983
83582
|
);
|
|
82984
83583
|
} catch (err) {
|
|
82985
83584
|
runtimeLog.warn(
|
|
82986
|
-
`Global unpause: failed to dispatch
|
|
83585
|
+
`Global unpause: failed to dispatch resumeAfterUnpause: ${err instanceof Error ? err.message : String(err)}`
|
|
82987
83586
|
);
|
|
82988
83587
|
}
|
|
82989
83588
|
if (s.autoMerge) {
|
|
@@ -83007,13 +83606,13 @@ ${detail}`
|
|
|
83007
83606
|
if (prev.enginePaused && !s.enginePaused) {
|
|
83008
83607
|
runtimeLog.log("Engine unpaused \u2014 resuming agentic activity");
|
|
83009
83608
|
try {
|
|
83010
|
-
const
|
|
83011
|
-
|
|
83012
|
-
(err) => runtimeLog.error("Failed to resume
|
|
83609
|
+
const runtime = this.runtime;
|
|
83610
|
+
runtime.resumeAfterUnpause?.().catch(
|
|
83611
|
+
(err) => runtimeLog.error("Failed to resume agentic activity on engine unpause:", err)
|
|
83013
83612
|
);
|
|
83014
83613
|
} catch (err) {
|
|
83015
83614
|
runtimeLog.warn(
|
|
83016
|
-
`Engine unpause: failed to dispatch
|
|
83615
|
+
`Engine unpause: failed to dispatch resumeAfterUnpause: ${err instanceof Error ? err.message : String(err)}`
|
|
83017
83616
|
);
|
|
83018
83617
|
}
|
|
83019
83618
|
if (s.autoMerge) {
|
|
@@ -85733,7 +86332,7 @@ var init_src3 = __esm({
|
|
|
85733
86332
|
});
|
|
85734
86333
|
|
|
85735
86334
|
// ../../plugins/fusion-plugin-hermes-runtime/dist/cli-spawn.js
|
|
85736
|
-
import { spawn as spawn6, spawnSync } from "node:child_process";
|
|
86335
|
+
import { spawn as spawn6, spawnSync as spawnSync2 } from "node:child_process";
|
|
85737
86336
|
import os2 from "node:os";
|
|
85738
86337
|
import path, { sep as PATH_SEP } from "node:path";
|
|
85739
86338
|
function resolveBinaryForSpawn(binary) {
|
|
@@ -85746,7 +86345,7 @@ function resolveBinaryForSpawn(binary) {
|
|
|
85746
86345
|
if (cached)
|
|
85747
86346
|
return cached;
|
|
85748
86347
|
try {
|
|
85749
|
-
const result =
|
|
86348
|
+
const result = spawnSync2("where", [binary], { encoding: "utf-8" });
|
|
85750
86349
|
if (result.status === 0) {
|
|
85751
86350
|
const first = (result.stdout ?? "").trim().split(/\r?\n/)[0];
|
|
85752
86351
|
if (first?.length) {
|
|
@@ -98615,7 +99214,8 @@ async function clearDefaultProject(globalDir) {
|
|
|
98615
99214
|
await globalStore.updateSettings(rest);
|
|
98616
99215
|
}
|
|
98617
99216
|
async function detectProjectFromCwd(cwd, central) {
|
|
98618
|
-
|
|
99217
|
+
const startDir = resolve17(cwd);
|
|
99218
|
+
let currentDir = startDir;
|
|
98619
99219
|
while (true) {
|
|
98620
99220
|
const kbPath = resolve17(currentDir, ".fusion", "fusion.db");
|
|
98621
99221
|
if (isValidSqliteDatabaseFile(kbPath)) {
|
|
@@ -98623,11 +99223,13 @@ async function detectProjectFromCwd(cwd, central) {
|
|
|
98623
99223
|
if (project) {
|
|
98624
99224
|
return project;
|
|
98625
99225
|
}
|
|
98626
|
-
|
|
98627
|
-
|
|
98628
|
-
|
|
98629
|
-
|
|
98630
|
-
|
|
99226
|
+
if (currentDir === startDir) {
|
|
99227
|
+
return {
|
|
99228
|
+
id: "",
|
|
99229
|
+
name: basename9(currentDir) || "current-project",
|
|
99230
|
+
path: currentDir
|
|
99231
|
+
};
|
|
99232
|
+
}
|
|
98631
99233
|
}
|
|
98632
99234
|
const parentDir = dirname12(currentDir);
|
|
98633
99235
|
if (parentDir === currentDir) {
|