@runfusion/fusion 0.5.0 → 0.6.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 +799 -430
- package/dist/client/assets/{AgentDetailView-CnedHKqd.js → AgentDetailView-PYfYi6rl.js} +1 -1
- package/dist/client/assets/{AgentsView-TCAMvB-N.js → AgentsView-CjqSko8c.js} +3 -3
- package/dist/client/assets/{ChatView-B7djAFGF.js → ChatView-CY7Mdd3y.js} +1 -1
- package/dist/client/assets/{DevServerView-B_zgnvEn.js → DevServerView-Jt4PTXWB.js} +1 -1
- package/dist/client/assets/{DirectoryPicker-B3ejJOl0.js → DirectoryPicker-Bcoh2_ft.js} +1 -1
- package/dist/client/assets/{DocumentsView-DzmbEKrl.js → DocumentsView-B2YRSmIS.js} +1 -1
- package/dist/client/assets/{InsightsView-CoRsf8DC.js → InsightsView-C4-vGe-H.js} +1 -1
- package/dist/client/assets/{MemoryView-oFUIaoM5.js → MemoryView-Ch7agzuP.js} +1 -1
- package/dist/client/assets/{NodesView-BXUaViVf.js → NodesView-D9rm5-_6.js} +1 -1
- package/dist/client/assets/{PiExtensionsManager-DJRC5zRv.js → PiExtensionsManager-hpV1-dOB.js} +1 -1
- package/dist/client/assets/{PluginManager-OQhiY2zP.js → PluginManager-DHHM1Xeg.js} +1 -1
- package/dist/client/assets/{RoadmapsView-DwdewIMm.js → RoadmapsView-BpqARpKn.js} +1 -1
- package/dist/client/assets/{SettingsModal-Bmz_YrW5.js → SettingsModal-B8Q5r_TT.js} +3 -3
- package/dist/client/assets/{SettingsModal-CMRJwSFd.js → SettingsModal-DFitE2kE.js} +1 -1
- package/dist/client/assets/{SetupWizardModal-BnPgyQho.js → SetupWizardModal-CjUbb4VZ.js} +1 -1
- package/dist/client/assets/{SkillsView-pHEyP-vg.js → SkillsView-CaQNaA67.js} +1 -1
- package/dist/client/assets/{TodoView-Cg-xDGeH.js → TodoView-Bj0DcDdf.js} +1 -1
- package/dist/client/assets/{folder-open-DJxjQmf1.js → folder-open-DDanUhRq.js} +1 -1
- package/dist/client/assets/{index-Cf4wCcWp.js → index-BRTEq_-7.js} +5 -5
- package/dist/client/assets/index-w9ci2GQ7.css +1 -0
- package/dist/client/assets/{list-checks-NjVTpQgK.js → list-checks-eKLuZO4Y.js} +1 -1
- package/dist/client/assets/{upload-BQA5FG0G.js → upload-DDJr7Sat.js} +1 -1
- package/dist/client/assets/{users-p7CqLoIz.js → users-zMb4VibZ.js} +1 -1
- package/dist/client/index.html +2 -2
- package/dist/client/version.json +1 -1
- package/dist/extension.js +352 -154
- package/dist/pi-claude-cli/package.json +1 -1
- package/package.json +5 -5
- package/dist/client/assets/index-B7TDbn3p.css +0 -1
package/dist/extension.js
CHANGED
|
@@ -164,6 +164,8 @@ var init_settings_schema = __esm({
|
|
|
164
164
|
archiveAgentLogMode: "compact",
|
|
165
165
|
autoUpdatePrStatus: false,
|
|
166
166
|
autoCreatePr: false,
|
|
167
|
+
githubCommentOnDone: false,
|
|
168
|
+
githubCommentTemplate: void 0,
|
|
167
169
|
autoBackupEnabled: false,
|
|
168
170
|
autoBackupSchedule: "0 2 * * *",
|
|
169
171
|
autoBackupRetention: 7,
|
|
@@ -2274,8 +2276,8 @@ function normalizeTaskComments(steeringComments, comments) {
|
|
|
2274
2276
|
comments: normalizedComments
|
|
2275
2277
|
};
|
|
2276
2278
|
}
|
|
2277
|
-
function createDatabase(fusionDir) {
|
|
2278
|
-
return new Database(fusionDir);
|
|
2279
|
+
function createDatabase(fusionDir, options) {
|
|
2280
|
+
return new Database(fusionDir, options);
|
|
2279
2281
|
}
|
|
2280
2282
|
var SCHEMA_VERSION, SCHEMA_SQL, Database;
|
|
2281
2283
|
var init_db = __esm({
|
|
@@ -2283,7 +2285,7 @@ var init_db = __esm({
|
|
|
2283
2285
|
"use strict";
|
|
2284
2286
|
init_sqlite_adapter();
|
|
2285
2287
|
init_types();
|
|
2286
|
-
SCHEMA_VERSION =
|
|
2288
|
+
SCHEMA_VERSION = 48;
|
|
2287
2289
|
SCHEMA_SQL = `
|
|
2288
2290
|
-- Tasks table with JSON columns for nested data
|
|
2289
2291
|
CREATE TABLE IF NOT EXISTS tasks (
|
|
@@ -2791,16 +2793,19 @@ CREATE INDEX IF NOT EXISTS idxTodoItemsSortOrder ON todo_items(listId, sortOrder
|
|
|
2791
2793
|
/** Tracks transaction nesting depth for savepoint-based nested transactions. */
|
|
2792
2794
|
transactionDepth = 0;
|
|
2793
2795
|
_fts5Available;
|
|
2794
|
-
constructor(fusionDir) {
|
|
2795
|
-
|
|
2796
|
-
|
|
2796
|
+
constructor(fusionDir, options) {
|
|
2797
|
+
const inMemory = options?.inMemory === true;
|
|
2798
|
+
this.dbPath = inMemory ? ":memory:" : join(fusionDir, "fusion.db");
|
|
2799
|
+
if (!inMemory && !isAbsolute(fusionDir)) {
|
|
2797
2800
|
throw new Error(`[fusion] Database constructor requires an absolute fusionDir path, got: ${fusionDir}`);
|
|
2798
2801
|
}
|
|
2799
|
-
if (!existsSync(fusionDir)) {
|
|
2802
|
+
if (!inMemory && !existsSync(fusionDir)) {
|
|
2800
2803
|
mkdirSync(fusionDir, { recursive: true });
|
|
2801
2804
|
}
|
|
2802
2805
|
this.db = new DatabaseSync(this.dbPath);
|
|
2803
|
-
|
|
2806
|
+
if (!inMemory) {
|
|
2807
|
+
this.db.exec("PRAGMA journal_mode = WAL");
|
|
2808
|
+
}
|
|
2804
2809
|
this.db.exec("PRAGMA busy_timeout = 5000");
|
|
2805
2810
|
this.db.exec("PRAGMA foreign_keys = ON");
|
|
2806
2811
|
this._fts5Available = probeFts5(this.db);
|
|
@@ -3729,6 +3734,11 @@ CREATE INDEX IF NOT EXISTS idxTodoItemsSortOrder ON todo_items(listId, sortOrder
|
|
|
3729
3734
|
}
|
|
3730
3735
|
});
|
|
3731
3736
|
}
|
|
3737
|
+
if (version < 48) {
|
|
3738
|
+
this.applyMigration(48, () => {
|
|
3739
|
+
this.addColumnIfMissing("tasks", "verificationFailureCount", "INTEGER DEFAULT 0");
|
|
3740
|
+
});
|
|
3741
|
+
}
|
|
3732
3742
|
}
|
|
3733
3743
|
/**
|
|
3734
3744
|
* Run a single migration step inside a transaction and bump the version.
|
|
@@ -3918,6 +3928,7 @@ var init_agent_store = __esm({
|
|
|
3918
3928
|
locks = /* @__PURE__ */ new Map();
|
|
3919
3929
|
_db = null;
|
|
3920
3930
|
taskStore;
|
|
3931
|
+
inMemoryDb;
|
|
3921
3932
|
constructor(options = {}) {
|
|
3922
3933
|
super();
|
|
3923
3934
|
if (!options.rootDir && process.env.VITEST === "true") {
|
|
@@ -3928,10 +3939,11 @@ var init_agent_store = __esm({
|
|
|
3928
3939
|
this.rootDir = options.rootDir ?? resolve(".fusion");
|
|
3929
3940
|
this.agentsDir = join2(this.rootDir, "agents");
|
|
3930
3941
|
this.taskStore = options.taskStore;
|
|
3942
|
+
this.inMemoryDb = options.inMemoryDb === true;
|
|
3931
3943
|
}
|
|
3932
3944
|
get db() {
|
|
3933
3945
|
if (!this._db) {
|
|
3934
|
-
this._db = new Database(this.rootDir);
|
|
3946
|
+
this._db = new Database(this.rootDir, { inMemory: this.inMemoryDb });
|
|
3935
3947
|
this._db.init();
|
|
3936
3948
|
}
|
|
3937
3949
|
return this._db;
|
|
@@ -6231,9 +6243,9 @@ var init_global_settings = __esm({
|
|
|
6231
6243
|
* Serialize operations via promise chain to prevent lost-update races.
|
|
6232
6244
|
*/
|
|
6233
6245
|
withLock(fn) {
|
|
6234
|
-
let
|
|
6246
|
+
let resolve17;
|
|
6235
6247
|
const next = new Promise((r) => {
|
|
6236
|
-
|
|
6248
|
+
resolve17 = r;
|
|
6237
6249
|
});
|
|
6238
6250
|
const prev = this.lock;
|
|
6239
6251
|
this.lock = next;
|
|
@@ -6241,7 +6253,7 @@ var init_global_settings = __esm({
|
|
|
6241
6253
|
try {
|
|
6242
6254
|
return await fn();
|
|
6243
6255
|
} finally {
|
|
6244
|
-
|
|
6256
|
+
resolve17();
|
|
6245
6257
|
}
|
|
6246
6258
|
});
|
|
6247
6259
|
}
|
|
@@ -6305,12 +6317,15 @@ END;
|
|
|
6305
6317
|
ArchiveDatabase = class {
|
|
6306
6318
|
db;
|
|
6307
6319
|
_fts5Available;
|
|
6308
|
-
constructor(fusionDir) {
|
|
6309
|
-
|
|
6320
|
+
constructor(fusionDir, options) {
|
|
6321
|
+
const inMemory = options?.inMemory === true;
|
|
6322
|
+
if (!inMemory && !existsSync4(fusionDir)) {
|
|
6310
6323
|
mkdirSync3(fusionDir, { recursive: true });
|
|
6311
6324
|
}
|
|
6312
|
-
this.db = new DatabaseSync(join5(fusionDir, "archive.db"));
|
|
6313
|
-
|
|
6325
|
+
this.db = new DatabaseSync(inMemory ? ":memory:" : join5(fusionDir, "archive.db"));
|
|
6326
|
+
if (!inMemory) {
|
|
6327
|
+
this.db.exec("PRAGMA journal_mode = WAL");
|
|
6328
|
+
}
|
|
6314
6329
|
this.db.exec("PRAGMA busy_timeout = 5000");
|
|
6315
6330
|
this._fts5Available = probeFts5(this.db);
|
|
6316
6331
|
}
|
|
@@ -9525,19 +9540,21 @@ var init_plugin_store = __esm({
|
|
|
9525
9540
|
init_db();
|
|
9526
9541
|
init_plugin_types();
|
|
9527
9542
|
PluginStore = class extends EventEmitter5 {
|
|
9528
|
-
constructor(rootDir) {
|
|
9543
|
+
constructor(rootDir, options) {
|
|
9529
9544
|
super();
|
|
9530
9545
|
this.rootDir = rootDir;
|
|
9546
|
+
this.inMemoryDb = options?.inMemoryDb === true;
|
|
9531
9547
|
}
|
|
9532
9548
|
/** SQLite database instance */
|
|
9533
9549
|
_db = null;
|
|
9550
|
+
inMemoryDb;
|
|
9534
9551
|
/**
|
|
9535
9552
|
* Get the SQLite database, initializing it on first access.
|
|
9536
9553
|
*/
|
|
9537
9554
|
get db() {
|
|
9538
9555
|
if (!this._db) {
|
|
9539
9556
|
const fusionDir = join7(this.rootDir, ".fusion");
|
|
9540
|
-
this._db = new Database(fusionDir);
|
|
9557
|
+
this._db = new Database(fusionDir, { inMemory: this.inMemoryDb });
|
|
9541
9558
|
this._db.init();
|
|
9542
9559
|
}
|
|
9543
9560
|
return this._db;
|
|
@@ -27410,21 +27427,23 @@ var init_automation_store = __esm({
|
|
|
27410
27427
|
init_db();
|
|
27411
27428
|
CRON_TIMEZONE = "UTC";
|
|
27412
27429
|
AutomationStore = class _AutomationStore extends EventEmitter11 {
|
|
27413
|
-
constructor(rootDir) {
|
|
27430
|
+
constructor(rootDir, options) {
|
|
27414
27431
|
super();
|
|
27415
27432
|
this.rootDir = rootDir;
|
|
27433
|
+
this.inMemoryDb = options?.inMemoryDb === true;
|
|
27416
27434
|
}
|
|
27417
27435
|
/** Per-schedule promise chain for serializing writes. */
|
|
27418
27436
|
scheduleLocks = /* @__PURE__ */ new Map();
|
|
27419
27437
|
/** SQLite database instance */
|
|
27420
27438
|
_db = null;
|
|
27439
|
+
inMemoryDb;
|
|
27421
27440
|
/**
|
|
27422
27441
|
* Get the SQLite database, initializing it on first access.
|
|
27423
27442
|
*/
|
|
27424
27443
|
get db() {
|
|
27425
27444
|
if (!this._db) {
|
|
27426
27445
|
const fusionDir = join12(this.rootDir, ".fusion");
|
|
27427
|
-
this._db = new Database(fusionDir);
|
|
27446
|
+
this._db = new Database(fusionDir, { inMemory: this.inMemoryDb });
|
|
27428
27447
|
this._db.init();
|
|
27429
27448
|
}
|
|
27430
27449
|
return this._db;
|
|
@@ -27489,9 +27508,9 @@ var init_automation_store = __esm({
|
|
|
27489
27508
|
*/
|
|
27490
27509
|
withScheduleLock(id, fn) {
|
|
27491
27510
|
const prev = this.scheduleLocks.get(id) ?? Promise.resolve();
|
|
27492
|
-
let
|
|
27511
|
+
let resolve17;
|
|
27493
27512
|
const next = new Promise((r) => {
|
|
27494
|
-
|
|
27513
|
+
resolve17 = r;
|
|
27495
27514
|
});
|
|
27496
27515
|
this.scheduleLocks.set(id, next);
|
|
27497
27516
|
return prev.then(async () => {
|
|
@@ -27501,7 +27520,7 @@ var init_automation_store = __esm({
|
|
|
27501
27520
|
if (this.scheduleLocks.get(id) === next) {
|
|
27502
27521
|
this.scheduleLocks.delete(id);
|
|
27503
27522
|
}
|
|
27504
|
-
|
|
27523
|
+
resolve17();
|
|
27505
27524
|
}
|
|
27506
27525
|
});
|
|
27507
27526
|
}
|
|
@@ -29289,7 +29308,7 @@ var init_project_memory = __esm({
|
|
|
29289
29308
|
// ../core/src/run-command.ts
|
|
29290
29309
|
import { spawn } from "node:child_process";
|
|
29291
29310
|
function runCommandAsync(command, options = {}) {
|
|
29292
|
-
return new Promise((
|
|
29311
|
+
return new Promise((resolve17) => {
|
|
29293
29312
|
const maxBuffer = options.maxBuffer ?? DEFAULT_MAX_BUFFER;
|
|
29294
29313
|
let stdout = "";
|
|
29295
29314
|
let stderr = "";
|
|
@@ -29348,7 +29367,7 @@ function runCommandAsync(command, options = {}) {
|
|
|
29348
29367
|
clearTimeout(forceKillTimer);
|
|
29349
29368
|
forceKillTimer = null;
|
|
29350
29369
|
}
|
|
29351
|
-
|
|
29370
|
+
resolve17({
|
|
29352
29371
|
stdout,
|
|
29353
29372
|
stderr,
|
|
29354
29373
|
exitCode: null,
|
|
@@ -29366,7 +29385,7 @@ function runCommandAsync(command, options = {}) {
|
|
|
29366
29385
|
}
|
|
29367
29386
|
signalProcessGroup("SIGTERM");
|
|
29368
29387
|
scheduleForceKill(NORMAL_CLEANUP_FORCE_KILL_DELAY_MS);
|
|
29369
|
-
|
|
29388
|
+
resolve17({
|
|
29370
29389
|
stdout,
|
|
29371
29390
|
stderr,
|
|
29372
29391
|
exitCode: code,
|
|
@@ -29426,8 +29445,8 @@ function assertSafeGitBranchName(name) {
|
|
|
29426
29445
|
}
|
|
29427
29446
|
}
|
|
29428
29447
|
function assertSafeAbsolutePath(path) {
|
|
29429
|
-
const
|
|
29430
|
-
if (!path || path.length > 4096 || !
|
|
29448
|
+
const isAbsolute12 = path.startsWith("/") || /^[A-Za-z]:[\\/]/.test(path);
|
|
29449
|
+
if (!path || path.length > 4096 || !isAbsolute12 || path.startsWith("-") || // Reject shell metacharacters, quotes, control chars, and NULs.
|
|
29431
29450
|
/["'`$\n\r\t;&|<>()*?[\]{}\\\0]/.test(
|
|
29432
29451
|
path.replace(/^[A-Za-z]:/, "")
|
|
29433
29452
|
// ignore the drive-letter colon on Windows
|
|
@@ -29519,13 +29538,14 @@ var init_store = __esm({
|
|
|
29519
29538
|
}
|
|
29520
29539
|
};
|
|
29521
29540
|
TaskStore = class _TaskStore extends EventEmitter12 {
|
|
29522
|
-
constructor(rootDir, globalSettingsDir) {
|
|
29541
|
+
constructor(rootDir, globalSettingsDir, options) {
|
|
29523
29542
|
super();
|
|
29524
29543
|
this.rootDir = rootDir;
|
|
29525
29544
|
this.setMaxListeners(100);
|
|
29526
29545
|
this.fusionDir = join15(rootDir, ".fusion");
|
|
29527
29546
|
this.tasksDir = join15(this.fusionDir, "tasks");
|
|
29528
29547
|
this.configPath = join15(this.fusionDir, "config.json");
|
|
29548
|
+
this.inMemoryDb = options?.inMemoryDb === true;
|
|
29529
29549
|
const resolvedGlobalSettingsDir = globalSettingsDir ?? (process.env.VITEST === "true" ? join15(rootDir, ".fusion-global-settings") : void 0);
|
|
29530
29550
|
this.globalSettingsStore = new GlobalSettingsStore(resolvedGlobalSettingsDir);
|
|
29531
29551
|
}
|
|
@@ -29568,6 +29588,7 @@ var init_store = __esm({
|
|
|
29568
29588
|
configPath;
|
|
29569
29589
|
/** SQLite database for structured data storage */
|
|
29570
29590
|
_db = null;
|
|
29591
|
+
activityListenersWired = false;
|
|
29571
29592
|
/** Separate SQLite database for compact archived task snapshots. */
|
|
29572
29593
|
_archiveDb = null;
|
|
29573
29594
|
/** File-system watcher instance */
|
|
@@ -29610,13 +29631,19 @@ var init_store = __esm({
|
|
|
29610
29631
|
insightStore = null;
|
|
29611
29632
|
/** Cached TodoStore instance */
|
|
29612
29633
|
todoStore = null;
|
|
29634
|
+
// Test-only: when true, both fusion.db and archive.db open as `:memory:`
|
|
29635
|
+
// SQLite connections instead of disk-backed files. Production code never
|
|
29636
|
+
// sets this; it's gated through an opt-in TaskStoreOptions field below.
|
|
29637
|
+
// Tests that need cross-instance persistence (open store A, close,
|
|
29638
|
+
// open store B on the same dir, expect data) must leave this false.
|
|
29639
|
+
inMemoryDb;
|
|
29613
29640
|
/**
|
|
29614
29641
|
* Get the SQLite database, initializing it on first access.
|
|
29615
29642
|
* Also performs auto-migration from legacy file-based storage if needed.
|
|
29616
29643
|
*/
|
|
29617
29644
|
get db() {
|
|
29618
29645
|
if (!this._db) {
|
|
29619
|
-
this._db = new Database(this.fusionDir);
|
|
29646
|
+
this._db = new Database(this.fusionDir, { inMemory: this.inMemoryDb });
|
|
29620
29647
|
this._db.init();
|
|
29621
29648
|
if (detectLegacyData(this.fusionDir)) {
|
|
29622
29649
|
}
|
|
@@ -29625,7 +29652,7 @@ var init_store = __esm({
|
|
|
29625
29652
|
}
|
|
29626
29653
|
get archiveDb() {
|
|
29627
29654
|
if (!this._archiveDb) {
|
|
29628
|
-
this._archiveDb = new ArchiveDatabase(this.fusionDir);
|
|
29655
|
+
this._archiveDb = new ArchiveDatabase(this.fusionDir, { inMemory: this.inMemoryDb });
|
|
29629
29656
|
this._archiveDb.init();
|
|
29630
29657
|
this.migrateLegacyArchiveEntriesToArchiveDb();
|
|
29631
29658
|
}
|
|
@@ -29634,7 +29661,7 @@ var init_store = __esm({
|
|
|
29634
29661
|
async init() {
|
|
29635
29662
|
await mkdir7(this.tasksDir, { recursive: true });
|
|
29636
29663
|
if (!this._db) {
|
|
29637
|
-
this._db = new Database(this.fusionDir);
|
|
29664
|
+
this._db = new Database(this.fusionDir, { inMemory: this.inMemoryDb });
|
|
29638
29665
|
this._db.init();
|
|
29639
29666
|
}
|
|
29640
29667
|
if (detectLegacyData(this.fusionDir)) {
|
|
@@ -29703,6 +29730,7 @@ var init_store = __esm({
|
|
|
29703
29730
|
postReviewFixCount: row.postReviewFixCount ?? void 0,
|
|
29704
29731
|
recoveryRetryCount: row.recoveryRetryCount ?? void 0,
|
|
29705
29732
|
taskDoneRetryCount: row.taskDoneRetryCount ?? void 0,
|
|
29733
|
+
verificationFailureCount: row.verificationFailureCount ?? void 0,
|
|
29706
29734
|
nextRecoveryAt: row.nextRecoveryAt || void 0,
|
|
29707
29735
|
error: row.error || void 0,
|
|
29708
29736
|
summary: row.summary || void 0,
|
|
@@ -29987,6 +30015,7 @@ ${recentText}` : void 0
|
|
|
29987
30015
|
"postReviewFixCount",
|
|
29988
30016
|
"recoveryRetryCount",
|
|
29989
30017
|
"taskDoneRetryCount",
|
|
30018
|
+
"verificationFailureCount",
|
|
29990
30019
|
"nextRecoveryAt",
|
|
29991
30020
|
"error",
|
|
29992
30021
|
"summary",
|
|
@@ -30056,6 +30085,7 @@ ${recentText}` : void 0
|
|
|
30056
30085
|
"postReviewFixCount",
|
|
30057
30086
|
"recoveryRetryCount",
|
|
30058
30087
|
"taskDoneRetryCount",
|
|
30088
|
+
"verificationFailureCount",
|
|
30059
30089
|
"nextRecoveryAt",
|
|
30060
30090
|
"error",
|
|
30061
30091
|
"summary",
|
|
@@ -30123,7 +30153,7 @@ ${recentText}` : void 0
|
|
|
30123
30153
|
id, title, description, priority, "column", status, size, reviewLevel, currentStep,
|
|
30124
30154
|
worktree, blockedBy, paused, baseBranch, branch, baseCommitSha, modelPresetId, modelProvider,
|
|
30125
30155
|
modelId, validatorModelProvider, validatorModelId, planningModelProvider, planningModelId, mergeRetries,
|
|
30126
|
-
workflowStepRetries, stuckKillCount, postReviewFixCount, recoveryRetryCount, taskDoneRetryCount, nextRecoveryAt, error,
|
|
30156
|
+
workflowStepRetries, stuckKillCount, postReviewFixCount, recoveryRetryCount, taskDoneRetryCount, verificationFailureCount, nextRecoveryAt, error,
|
|
30127
30157
|
summary, thinkingLevel, executionMode, tokenUsageInputTokens, tokenUsageOutputTokens, tokenUsageCachedTokens,
|
|
30128
30158
|
tokenUsageTotalTokens, tokenUsageFirstUsedAt, tokenUsageLastUsedAt, createdAt, updatedAt, columnMovedAt,
|
|
30129
30159
|
dependencies, steps, log, attachments, steeringComments,
|
|
@@ -30131,7 +30161,7 @@ ${recentText}` : void 0
|
|
|
30131
30161
|
sourceIssueProvider, sourceIssueRepository, sourceIssueExternalIssueId, sourceIssueNumber, sourceIssueUrl,
|
|
30132
30162
|
mergeDetails, breakIntoSubtasks, enabledWorkflowSteps, modifiedFiles, missionId, sliceId, assignedAgentId, assigneeUserId, checkedOutBy, checkedOutAt
|
|
30133
30163
|
) VALUES (
|
|
30134
|
-
?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?
|
|
30164
|
+
?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?
|
|
30135
30165
|
)
|
|
30136
30166
|
ON CONFLICT(id) DO UPDATE SET
|
|
30137
30167
|
title = excluded.title,
|
|
@@ -30161,6 +30191,7 @@ ${recentText}` : void 0
|
|
|
30161
30191
|
postReviewFixCount = excluded.postReviewFixCount,
|
|
30162
30192
|
recoveryRetryCount = excluded.recoveryRetryCount,
|
|
30163
30193
|
taskDoneRetryCount = excluded.taskDoneRetryCount,
|
|
30194
|
+
verificationFailureCount = excluded.verificationFailureCount,
|
|
30164
30195
|
nextRecoveryAt = excluded.nextRecoveryAt,
|
|
30165
30196
|
error = excluded.error,
|
|
30166
30197
|
summary = excluded.summary,
|
|
@@ -30228,6 +30259,7 @@ ${recentText}` : void 0
|
|
|
30228
30259
|
task.postReviewFixCount ?? 0,
|
|
30229
30260
|
task.recoveryRetryCount ?? null,
|
|
30230
30261
|
task.taskDoneRetryCount ?? 0,
|
|
30262
|
+
task.verificationFailureCount ?? 0,
|
|
30231
30263
|
task.nextRecoveryAt ?? null,
|
|
30232
30264
|
task.error ?? null,
|
|
30233
30265
|
task.summary ?? null,
|
|
@@ -30303,8 +30335,14 @@ ${recentText}` : void 0
|
|
|
30303
30335
|
/**
|
|
30304
30336
|
* Set up event listeners for activity logging.
|
|
30305
30337
|
* Call after init() to record task lifecycle events.
|
|
30338
|
+
*
|
|
30339
|
+
* Idempotent — repeated calls are no-ops. Without this guard, each duplicate
|
|
30340
|
+
* call double-registers handlers, causing the activity log to record every
|
|
30341
|
+
* `task:created` / `task:moved` event N times where N = number of init() calls.
|
|
30306
30342
|
*/
|
|
30307
30343
|
setupActivityLogListeners() {
|
|
30344
|
+
if (this.activityListenersWired) return;
|
|
30345
|
+
this.activityListenersWired = true;
|
|
30308
30346
|
this.on("task:created", (task) => {
|
|
30309
30347
|
this.recordActivityFromListener(
|
|
30310
30348
|
{
|
|
@@ -30408,9 +30446,9 @@ ${recentText}` : void 0
|
|
|
30408
30446
|
* lost-update races on the nextId counter.
|
|
30409
30447
|
*/
|
|
30410
30448
|
withConfigLock(fn) {
|
|
30411
|
-
let
|
|
30449
|
+
let resolve17;
|
|
30412
30450
|
const next = new Promise((r) => {
|
|
30413
|
-
|
|
30451
|
+
resolve17 = r;
|
|
30414
30452
|
});
|
|
30415
30453
|
const prev = this.configLock;
|
|
30416
30454
|
this.configLock = next;
|
|
@@ -30418,7 +30456,7 @@ ${recentText}` : void 0
|
|
|
30418
30456
|
try {
|
|
30419
30457
|
return await fn();
|
|
30420
30458
|
} finally {
|
|
30421
|
-
|
|
30459
|
+
resolve17();
|
|
30422
30460
|
}
|
|
30423
30461
|
});
|
|
30424
30462
|
}
|
|
@@ -30428,9 +30466,9 @@ ${recentText}` : void 0
|
|
|
30428
30466
|
*/
|
|
30429
30467
|
withTaskLock(id, fn) {
|
|
30430
30468
|
const prev = this.taskLocks.get(id) ?? Promise.resolve();
|
|
30431
|
-
let
|
|
30469
|
+
let resolve17;
|
|
30432
30470
|
const next = new Promise((r) => {
|
|
30433
|
-
|
|
30471
|
+
resolve17 = r;
|
|
30434
30472
|
});
|
|
30435
30473
|
this.taskLocks.set(id, next);
|
|
30436
30474
|
return prev.then(async () => {
|
|
@@ -30440,7 +30478,7 @@ ${recentText}` : void 0
|
|
|
30440
30478
|
if (this.taskLocks.get(id) === next) {
|
|
30441
30479
|
this.taskLocks.delete(id);
|
|
30442
30480
|
}
|
|
30443
|
-
|
|
30481
|
+
resolve17();
|
|
30444
30482
|
}
|
|
30445
30483
|
});
|
|
30446
30484
|
}
|
|
@@ -31550,6 +31588,11 @@ ${newTask.description}
|
|
|
31550
31588
|
} else if (updates.taskDoneRetryCount !== void 0) {
|
|
31551
31589
|
task.taskDoneRetryCount = updates.taskDoneRetryCount;
|
|
31552
31590
|
}
|
|
31591
|
+
if (updates.verificationFailureCount === null) {
|
|
31592
|
+
task.verificationFailureCount = void 0;
|
|
31593
|
+
} else if (updates.verificationFailureCount !== void 0) {
|
|
31594
|
+
task.verificationFailureCount = updates.verificationFailureCount;
|
|
31595
|
+
}
|
|
31553
31596
|
if (updates.nextRecoveryAt === null) {
|
|
31554
31597
|
task.nextRecoveryAt = void 0;
|
|
31555
31598
|
} else if (updates.nextRecoveryAt !== void 0) {
|
|
@@ -32541,7 +32584,7 @@ ${task.description}
|
|
|
32541
32584
|
this.emit("task:deleted", cached);
|
|
32542
32585
|
}
|
|
32543
32586
|
}
|
|
32544
|
-
await new Promise((
|
|
32587
|
+
await new Promise((resolve17) => setImmediate(resolve17));
|
|
32545
32588
|
const selectClause = this.getTaskSelectClause(true);
|
|
32546
32589
|
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();
|
|
32547
32590
|
this.lastPollTime = (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -32561,7 +32604,7 @@ ${task.description}
|
|
|
32561
32604
|
this.emit("task:updated", task);
|
|
32562
32605
|
}
|
|
32563
32606
|
if (i > 0 && i % 50 === 0) {
|
|
32564
|
-
await new Promise((
|
|
32607
|
+
await new Promise((resolve17) => setImmediate(resolve17));
|
|
32565
32608
|
}
|
|
32566
32609
|
}
|
|
32567
32610
|
const elapsed = Date.now() - startTime;
|
|
@@ -34382,7 +34425,7 @@ function runGh(args, cwd) {
|
|
|
34382
34425
|
}
|
|
34383
34426
|
}
|
|
34384
34427
|
function runGhAsync(args, cwd) {
|
|
34385
|
-
return new Promise((
|
|
34428
|
+
return new Promise((resolve17, reject) => {
|
|
34386
34429
|
execFile2(
|
|
34387
34430
|
"gh",
|
|
34388
34431
|
args,
|
|
@@ -34398,7 +34441,7 @@ function runGhAsync(args, cwd) {
|
|
|
34398
34441
|
ghError.stderr = stderr ?? "";
|
|
34399
34442
|
reject(ghError);
|
|
34400
34443
|
} else {
|
|
34401
|
-
|
|
34444
|
+
resolve17(stdout ?? "");
|
|
34402
34445
|
}
|
|
34403
34446
|
}
|
|
34404
34447
|
);
|
|
@@ -34516,14 +34559,16 @@ var init_routine_store = __esm({
|
|
|
34516
34559
|
init_routine();
|
|
34517
34560
|
CRON_TIMEZONE2 = "UTC";
|
|
34518
34561
|
RoutineStore = class _RoutineStore extends EventEmitter13 {
|
|
34519
|
-
constructor(rootDir) {
|
|
34562
|
+
constructor(rootDir, options) {
|
|
34520
34563
|
super();
|
|
34521
34564
|
this.rootDir = rootDir;
|
|
34565
|
+
this.inMemoryDb = options?.inMemoryDb === true;
|
|
34522
34566
|
}
|
|
34523
34567
|
/** SQLite database instance (lazy init). */
|
|
34524
34568
|
_db = null;
|
|
34525
34569
|
/** Per-routine promise chain for serializing writes. */
|
|
34526
34570
|
routineLocks = /* @__PURE__ */ new Map();
|
|
34571
|
+
inMemoryDb;
|
|
34527
34572
|
// ── Database Access ────────────────────────────────────────────────
|
|
34528
34573
|
/**
|
|
34529
34574
|
* Get the SQLite database, initializing it on first access.
|
|
@@ -34531,7 +34576,7 @@ var init_routine_store = __esm({
|
|
|
34531
34576
|
get db() {
|
|
34532
34577
|
if (!this._db) {
|
|
34533
34578
|
const fusionDir = `${this.rootDir}/.fusion`;
|
|
34534
|
-
this._db = new Database(fusionDir);
|
|
34579
|
+
this._db = new Database(fusionDir, { inMemory: this.inMemoryDb });
|
|
34535
34580
|
this._db.init();
|
|
34536
34581
|
}
|
|
34537
34582
|
return this._db;
|
|
@@ -34652,9 +34697,9 @@ var init_routine_store = __esm({
|
|
|
34652
34697
|
*/
|
|
34653
34698
|
withRoutineLock(id, fn) {
|
|
34654
34699
|
const prev = this.routineLocks.get(id) ?? Promise.resolve();
|
|
34655
|
-
let
|
|
34700
|
+
let resolve17;
|
|
34656
34701
|
const next = new Promise((r) => {
|
|
34657
|
-
|
|
34702
|
+
resolve17 = r;
|
|
34658
34703
|
});
|
|
34659
34704
|
this.routineLocks.set(id, next);
|
|
34660
34705
|
return prev.then(async () => {
|
|
@@ -34664,7 +34709,7 @@ var init_routine_store = __esm({
|
|
|
34664
34709
|
if (this.routineLocks.get(id) === next) {
|
|
34665
34710
|
this.routineLocks.delete(id);
|
|
34666
34711
|
}
|
|
34667
|
-
|
|
34712
|
+
resolve17();
|
|
34668
34713
|
}
|
|
34669
34714
|
});
|
|
34670
34715
|
}
|
|
@@ -35186,13 +35231,13 @@ var init_plugin_loader = __esm({
|
|
|
35186
35231
|
* Execute a promise with a timeout.
|
|
35187
35232
|
*/
|
|
35188
35233
|
withTimeout(promise, ms, timeoutMessage) {
|
|
35189
|
-
return new Promise((
|
|
35234
|
+
return new Promise((resolve17, reject) => {
|
|
35190
35235
|
const timer = setTimeout(() => {
|
|
35191
35236
|
reject(new Error(timeoutMessage));
|
|
35192
35237
|
}, ms);
|
|
35193
35238
|
promise.then((result) => {
|
|
35194
35239
|
clearTimeout(timer);
|
|
35195
|
-
|
|
35240
|
+
resolve17(result);
|
|
35196
35241
|
}).catch((err) => {
|
|
35197
35242
|
clearTimeout(timer);
|
|
35198
35243
|
reject(err);
|
|
@@ -38399,7 +38444,7 @@ var require_get_stream = __commonJS({
|
|
|
38399
38444
|
};
|
|
38400
38445
|
const { maxBuffer } = options;
|
|
38401
38446
|
let stream;
|
|
38402
|
-
await new Promise((
|
|
38447
|
+
await new Promise((resolve17, reject) => {
|
|
38403
38448
|
const rejectPromise = (error) => {
|
|
38404
38449
|
if (error && stream.getBufferedLength() <= BufferConstants.MAX_LENGTH) {
|
|
38405
38450
|
error.bufferedData = stream.getBufferedValue();
|
|
@@ -38411,7 +38456,7 @@ var require_get_stream = __commonJS({
|
|
|
38411
38456
|
rejectPromise(error);
|
|
38412
38457
|
return;
|
|
38413
38458
|
}
|
|
38414
|
-
|
|
38459
|
+
resolve17();
|
|
38415
38460
|
});
|
|
38416
38461
|
stream.on("data", () => {
|
|
38417
38462
|
if (stream.getBufferedLength() > maxBuffer) {
|
|
@@ -39705,7 +39750,7 @@ var require_extract_zip = __commonJS({
|
|
|
39705
39750
|
debug("opening", this.zipPath, "with opts", this.opts);
|
|
39706
39751
|
this.zipfile = await openZip(this.zipPath, { lazyEntries: true });
|
|
39707
39752
|
this.canceled = false;
|
|
39708
|
-
return new Promise((
|
|
39753
|
+
return new Promise((resolve17, reject) => {
|
|
39709
39754
|
this.zipfile.on("error", (err) => {
|
|
39710
39755
|
this.canceled = true;
|
|
39711
39756
|
reject(err);
|
|
@@ -39714,7 +39759,7 @@ var require_extract_zip = __commonJS({
|
|
|
39714
39759
|
this.zipfile.on("close", () => {
|
|
39715
39760
|
if (!this.canceled) {
|
|
39716
39761
|
debug("zip extraction complete");
|
|
39717
|
-
|
|
39762
|
+
resolve17();
|
|
39718
39763
|
}
|
|
39719
39764
|
});
|
|
39720
39765
|
this.zipfile.on("entry", async (entry) => {
|
|
@@ -49720,12 +49765,12 @@ var init_concurrency = __esm({
|
|
|
49720
49765
|
this._active++;
|
|
49721
49766
|
return Promise.resolve();
|
|
49722
49767
|
}
|
|
49723
|
-
return new Promise((
|
|
49768
|
+
return new Promise((resolve17) => {
|
|
49724
49769
|
this._waiters.push({
|
|
49725
49770
|
priority,
|
|
49726
49771
|
resolve: () => {
|
|
49727
49772
|
this._active++;
|
|
49728
|
-
|
|
49773
|
+
resolve17();
|
|
49729
49774
|
}
|
|
49730
49775
|
});
|
|
49731
49776
|
});
|
|
@@ -52145,20 +52190,20 @@ async function withRateLimitRetry(fn, options = {}) {
|
|
|
52145
52190
|
throw lastError ?? new Error("withRateLimitRetry: unexpected state");
|
|
52146
52191
|
}
|
|
52147
52192
|
function sleep(ms, signal) {
|
|
52148
|
-
return new Promise((
|
|
52193
|
+
return new Promise((resolve17, reject) => {
|
|
52149
52194
|
if (signal?.aborted) {
|
|
52150
52195
|
reject(signal.reason ?? new Error("Aborted"));
|
|
52151
52196
|
return;
|
|
52152
52197
|
}
|
|
52153
|
-
const timer = setTimeout(
|
|
52198
|
+
const timer = setTimeout(resolve17, ms);
|
|
52154
52199
|
if (signal) {
|
|
52155
52200
|
const onAbort = () => {
|
|
52156
52201
|
clearTimeout(timer);
|
|
52157
52202
|
reject(signal.reason ?? new Error("Aborted"));
|
|
52158
52203
|
};
|
|
52159
52204
|
signal.addEventListener("abort", onAbort, { once: true });
|
|
52160
|
-
const origResolve =
|
|
52161
|
-
|
|
52205
|
+
const origResolve = resolve17;
|
|
52206
|
+
resolve17 = () => {
|
|
52162
52207
|
signal.removeEventListener("abort", onAbort);
|
|
52163
52208
|
origResolve();
|
|
52164
52209
|
};
|
|
@@ -54053,7 +54098,14 @@ import { existsSync as existsSync21 } from "node:fs";
|
|
|
54053
54098
|
import { join as join26 } from "node:path";
|
|
54054
54099
|
import { Type as Type3 } from "typebox";
|
|
54055
54100
|
async function execWithProcessGroup(command, options) {
|
|
54056
|
-
return new Promise((
|
|
54101
|
+
return new Promise((resolve17, reject) => {
|
|
54102
|
+
if (options.signal?.aborted) {
|
|
54103
|
+
reject(Object.assign(
|
|
54104
|
+
new Error(`Command aborted before start: ${command}`),
|
|
54105
|
+
{ code: "ABORT_ERR", aborted: true, stdout: "", stderr: "" }
|
|
54106
|
+
));
|
|
54107
|
+
return;
|
|
54108
|
+
}
|
|
54057
54109
|
const child = spawn2(command, {
|
|
54058
54110
|
cwd: options.cwd,
|
|
54059
54111
|
shell: true,
|
|
@@ -54065,24 +54117,33 @@ async function execWithProcessGroup(command, options) {
|
|
|
54065
54117
|
let stdoutOverflow = false;
|
|
54066
54118
|
let stderrOverflow = false;
|
|
54067
54119
|
let timedOut = false;
|
|
54120
|
+
let aborted = false;
|
|
54068
54121
|
let settled = false;
|
|
54122
|
+
const killTree = (sig) => {
|
|
54123
|
+
if (child.pid === void 0) return;
|
|
54124
|
+
try {
|
|
54125
|
+
process.kill(-child.pid, sig);
|
|
54126
|
+
} catch {
|
|
54127
|
+
}
|
|
54128
|
+
};
|
|
54069
54129
|
const timer = setTimeout(() => {
|
|
54070
54130
|
timedOut = true;
|
|
54071
|
-
|
|
54072
|
-
|
|
54073
|
-
|
|
54074
|
-
|
|
54075
|
-
|
|
54076
|
-
setTimeout(() => {
|
|
54077
|
-
if (settled) return;
|
|
54078
|
-
try {
|
|
54079
|
-
process.kill(-child.pid, "SIGKILL");
|
|
54080
|
-
} catch {
|
|
54081
|
-
}
|
|
54082
|
-
}, 5e3).unref();
|
|
54083
|
-
}
|
|
54131
|
+
killTree("SIGTERM");
|
|
54132
|
+
setTimeout(() => {
|
|
54133
|
+
if (settled) return;
|
|
54134
|
+
killTree("SIGKILL");
|
|
54135
|
+
}, 5e3).unref();
|
|
54084
54136
|
}, options.timeout);
|
|
54085
54137
|
timer.unref();
|
|
54138
|
+
const onAbort = () => {
|
|
54139
|
+
aborted = true;
|
|
54140
|
+
killTree("SIGTERM");
|
|
54141
|
+
setTimeout(() => {
|
|
54142
|
+
if (settled) return;
|
|
54143
|
+
killTree("SIGKILL");
|
|
54144
|
+
}, 5e3).unref();
|
|
54145
|
+
};
|
|
54146
|
+
options.signal?.addEventListener("abort", onAbort, { once: true });
|
|
54086
54147
|
child.stdout?.on("data", (chunk) => {
|
|
54087
54148
|
if (stdoutOverflow) return;
|
|
54088
54149
|
if (stdout.length + chunk.length > options.maxBuffer) {
|
|
@@ -54105,6 +54166,14 @@ async function execWithProcessGroup(command, options) {
|
|
|
54105
54166
|
if (settled) return;
|
|
54106
54167
|
settled = true;
|
|
54107
54168
|
clearTimeout(timer);
|
|
54169
|
+
options.signal?.removeEventListener("abort", onAbort);
|
|
54170
|
+
if (aborted) {
|
|
54171
|
+
reject(Object.assign(
|
|
54172
|
+
new Error(`Command aborted: ${command}`),
|
|
54173
|
+
{ code: "ABORT_ERR", aborted: true, stdout, stderr, killed: true }
|
|
54174
|
+
));
|
|
54175
|
+
return;
|
|
54176
|
+
}
|
|
54108
54177
|
if (timedOut) {
|
|
54109
54178
|
reject(Object.assign(
|
|
54110
54179
|
new Error(`Command timed out after ${options.timeout}ms: ${command}`),
|
|
@@ -54117,7 +54186,7 @@ async function execWithProcessGroup(command, options) {
|
|
|
54117
54186
|
return;
|
|
54118
54187
|
}
|
|
54119
54188
|
if (code === 0) {
|
|
54120
|
-
|
|
54189
|
+
resolve17({ stdout, stderr, bufferOverflow: stdoutOverflow || stderrOverflow });
|
|
54121
54190
|
return;
|
|
54122
54191
|
}
|
|
54123
54192
|
reject(Object.assign(
|
|
@@ -54458,7 +54527,8 @@ async function runVerificationCommand(store, rootDir, taskId, command, type, sig
|
|
|
54458
54527
|
const { stdout, stderr, bufferOverflow } = await execWithProcessGroup(command, {
|
|
54459
54528
|
cwd: rootDir,
|
|
54460
54529
|
timeout: VERIFICATION_COMMAND_TIMEOUT_MS,
|
|
54461
|
-
maxBuffer: VERIFICATION_COMMAND_MAX_BUFFER
|
|
54530
|
+
maxBuffer: VERIFICATION_COMMAND_MAX_BUFFER,
|
|
54531
|
+
signal
|
|
54462
54532
|
});
|
|
54463
54533
|
throwIfAborted(signal, taskId);
|
|
54464
54534
|
result.stdout = stdout?.toString?.() || "";
|
|
@@ -57575,7 +57645,7 @@ function resolveExecutorModelPair(taskModelProvider, taskModelId, settings) {
|
|
|
57575
57645
|
return { provider: void 0, modelId: void 0 };
|
|
57576
57646
|
}
|
|
57577
57647
|
function sleep2(ms) {
|
|
57578
|
-
return new Promise((
|
|
57648
|
+
return new Promise((resolve17) => setTimeout(resolve17, ms));
|
|
57579
57649
|
}
|
|
57580
57650
|
var execAsync4, stepExecLog, MAX_STEP_RETRIES, RETRY_DELAYS_MS, NOOP_TASK_STORE, StepSessionExecutor;
|
|
57581
57651
|
var init_step_session_executor = __esm({
|
|
@@ -61378,7 +61448,7 @@ Review the work done in this worktree and evaluate it against the criteria in yo
|
|
|
61378
61448
|
);
|
|
61379
61449
|
}
|
|
61380
61450
|
const delay2 = this.WORKTREE_RETRY_DELAYS[attempt] || 1e3;
|
|
61381
|
-
await new Promise((
|
|
61451
|
+
await new Promise((resolve17) => setTimeout(resolve17, delay2));
|
|
61382
61452
|
}
|
|
61383
61453
|
}
|
|
61384
61454
|
throw new Error("Unexpected exit from worktree creation retry loop");
|
|
@@ -68266,8 +68336,8 @@ ${taskDetail.prompt}` : "No PROMPT.md available.",
|
|
|
68266
68336
|
// ../engine/src/self-healing.ts
|
|
68267
68337
|
import { exec as exec8 } from "node:child_process";
|
|
68268
68338
|
import { promisify as promisify9 } from "node:util";
|
|
68269
|
-
import { existsSync as existsSync27, readdirSync as readdirSync5, statSync as statSync5 } from "node:fs";
|
|
68270
|
-
import { join as join33 } from "node:path";
|
|
68339
|
+
import { existsSync as existsSync27, readdirSync as readdirSync5, rmSync as rmSync3, statSync as statSync5 } from "node:fs";
|
|
68340
|
+
import { isAbsolute as isAbsolute11, join as join33, relative as relative7, resolve as resolve14 } from "node:path";
|
|
68271
68341
|
function shellQuote(value) {
|
|
68272
68342
|
return `'${value.replace(/'/g, "'\\''")}'`;
|
|
68273
68343
|
}
|
|
@@ -69423,15 +69493,26 @@ var init_self_healing = __esm({
|
|
|
69423
69493
|
log7.error(`Worktree prune failed: ${errorMessage}`);
|
|
69424
69494
|
}
|
|
69425
69495
|
}
|
|
69426
|
-
/**
|
|
69496
|
+
/**
|
|
69497
|
+
* Remove orphaned worktrees not assigned to any active task.
|
|
69498
|
+
*
|
|
69499
|
+
* When `recycleWorktrees` is OFF: removes registered idle worktrees too —
|
|
69500
|
+
* they would otherwise pile up since the pool isn't keeping them.
|
|
69501
|
+
*
|
|
69502
|
+
* When `recycleWorktrees` is ON: leaves registered idle worktrees alone
|
|
69503
|
+
* (the pool wants them for reuse) but still reaps unregistered stale dirs
|
|
69504
|
+
* left behind by killed runs (e.g., `clear-hawk-broken`, `*-bak`). Those
|
|
69505
|
+
* dirs can never be recycled — they aren't git worktrees — so they only
|
|
69506
|
+
* waste disk.
|
|
69507
|
+
*/
|
|
69427
69508
|
async cleanupOrphans() {
|
|
69428
69509
|
try {
|
|
69429
|
-
const orphaned = await scanIdleWorktrees(this.options.rootDir, this.store);
|
|
69430
|
-
if (orphaned.length === 0) return 0;
|
|
69431
69510
|
const settings = await this.store.getSettings();
|
|
69432
69511
|
if (settings.recycleWorktrees) {
|
|
69433
|
-
return
|
|
69512
|
+
return await this.reapUnregisteredOrphans();
|
|
69434
69513
|
}
|
|
69514
|
+
const orphaned = await scanIdleWorktrees(this.options.rootDir, this.store);
|
|
69515
|
+
if (orphaned.length === 0) return 0;
|
|
69435
69516
|
let cleaned = 0;
|
|
69436
69517
|
for (const worktreePath of orphaned) {
|
|
69437
69518
|
try {
|
|
@@ -69455,6 +69536,45 @@ var init_self_healing = __esm({
|
|
|
69455
69536
|
return 0;
|
|
69456
69537
|
}
|
|
69457
69538
|
}
|
|
69539
|
+
/**
|
|
69540
|
+
* Sweep unregistered stale directories under `<rootDir>/.worktrees/` —
|
|
69541
|
+
* directories that exist on disk but are NOT registered git worktrees.
|
|
69542
|
+
* Safe to run alongside `recycleWorktrees: true` because the pool only
|
|
69543
|
+
* tracks registered idle worktrees, never these orphans.
|
|
69544
|
+
*/
|
|
69545
|
+
async reapUnregisteredOrphans() {
|
|
69546
|
+
const worktreesDir = join33(this.options.rootDir, ".worktrees");
|
|
69547
|
+
if (!existsSync27(worktreesDir)) return 0;
|
|
69548
|
+
let dirs;
|
|
69549
|
+
try {
|
|
69550
|
+
dirs = readdirSync5(worktreesDir, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => join33(worktreesDir, e.name));
|
|
69551
|
+
} catch (err) {
|
|
69552
|
+
log7.warn(`Failed to read .worktrees/ for unregistered orphan reap: ${err instanceof Error ? err.message : String(err)}`);
|
|
69553
|
+
return 0;
|
|
69554
|
+
}
|
|
69555
|
+
if (dirs.length === 0) return 0;
|
|
69556
|
+
const registered = await getRegisteredWorktreePaths(this.options.rootDir);
|
|
69557
|
+
const unregistered = dirs.filter((d) => !registered.has(resolve14(d)));
|
|
69558
|
+
let cleaned = 0;
|
|
69559
|
+
for (const path of unregistered) {
|
|
69560
|
+
const rel = relative7(worktreesDir, path);
|
|
69561
|
+
if (rel === "" || rel.startsWith("..") || isAbsolute11(rel)) {
|
|
69562
|
+
log7.warn(`Refusing to remove path outside .worktrees: ${path}`);
|
|
69563
|
+
continue;
|
|
69564
|
+
}
|
|
69565
|
+
try {
|
|
69566
|
+
rmSync3(path, { recursive: true, force: true });
|
|
69567
|
+
log7.log(`Cleaned unregistered worktree dir: ${path}`);
|
|
69568
|
+
cleaned++;
|
|
69569
|
+
} catch (err) {
|
|
69570
|
+
log7.warn(`Failed to remove unregistered worktree dir ${path}: ${err instanceof Error ? err.message : String(err)}`);
|
|
69571
|
+
}
|
|
69572
|
+
}
|
|
69573
|
+
if (cleaned > 0) {
|
|
69574
|
+
log7.log(`Cleaned ${cleaned} unregistered worktree dir(s) (recycle mode preserves registered idle worktrees)`);
|
|
69575
|
+
}
|
|
69576
|
+
return cleaned;
|
|
69577
|
+
}
|
|
69458
69578
|
/**
|
|
69459
69579
|
* Remove orphaned `fusion/*` branches that are not associated with any
|
|
69460
69580
|
* active (non-archived, non-merger-managed) task.
|
|
@@ -70071,13 +70191,13 @@ var init_plugin_runner = __esm({
|
|
|
70071
70191
|
* Returns the result on success, throws on timeout.
|
|
70072
70192
|
*/
|
|
70073
70193
|
withTimeout(promise, ms, timeoutMessage) {
|
|
70074
|
-
return new Promise((
|
|
70194
|
+
return new Promise((resolve17, reject) => {
|
|
70075
70195
|
const timer = setTimeout(() => {
|
|
70076
70196
|
reject(new Error(timeoutMessage));
|
|
70077
70197
|
}, ms);
|
|
70078
70198
|
promise.then((result) => {
|
|
70079
70199
|
clearTimeout(timer);
|
|
70080
|
-
|
|
70200
|
+
resolve17(result);
|
|
70081
70201
|
}).catch((err) => {
|
|
70082
70202
|
clearTimeout(timer);
|
|
70083
70203
|
reject(err);
|
|
@@ -70735,7 +70855,7 @@ var init_in_process_runtime = __esm({
|
|
|
70735
70855
|
runtimeLog.log(
|
|
70736
70856
|
`Waiting for ${metrics.inFlightTasks} in-flight tasks to complete...`
|
|
70737
70857
|
);
|
|
70738
|
-
await new Promise((
|
|
70858
|
+
await new Promise((resolve17) => setTimeout(resolve17, 1e3));
|
|
70739
70859
|
}
|
|
70740
70860
|
const finalMetrics = this.getMetrics();
|
|
70741
70861
|
if (finalMetrics.inFlightTasks > 0) {
|
|
@@ -71124,13 +71244,13 @@ var init_ipc_host = __esm({
|
|
|
71124
71244
|
}
|
|
71125
71245
|
const id = generateCorrelationId();
|
|
71126
71246
|
const message = { type, id, payload };
|
|
71127
|
-
return new Promise((
|
|
71247
|
+
return new Promise((resolve17, reject) => {
|
|
71128
71248
|
const timeout = setTimeout(() => {
|
|
71129
71249
|
this.pendingCommands.delete(id);
|
|
71130
71250
|
reject(new Error(`Command ${type} timed out after ${timeoutMs ?? this.commandTimeoutMs}ms`));
|
|
71131
71251
|
}, timeoutMs ?? this.commandTimeoutMs);
|
|
71132
71252
|
this.pendingCommands.set(id, {
|
|
71133
|
-
resolve:
|
|
71253
|
+
resolve: resolve17,
|
|
71134
71254
|
reject,
|
|
71135
71255
|
timeout,
|
|
71136
71256
|
type
|
|
@@ -71939,8 +72059,8 @@ var init_remote_node_client = __esm({
|
|
|
71939
72059
|
return error instanceof TypeError;
|
|
71940
72060
|
}
|
|
71941
72061
|
async sleep(ms) {
|
|
71942
|
-
await new Promise((
|
|
71943
|
-
setTimeout(
|
|
72062
|
+
await new Promise((resolve17) => {
|
|
72063
|
+
setTimeout(resolve17, ms);
|
|
71944
72064
|
});
|
|
71945
72065
|
}
|
|
71946
72066
|
};
|
|
@@ -72204,14 +72324,14 @@ var init_remote_node_runtime = __esm({
|
|
|
72204
72324
|
return error instanceof Error ? error : new Error(String(error));
|
|
72205
72325
|
}
|
|
72206
72326
|
async sleep(ms, signal) {
|
|
72207
|
-
await new Promise((
|
|
72327
|
+
await new Promise((resolve17) => {
|
|
72208
72328
|
const timeout = setTimeout(() => {
|
|
72209
72329
|
cleanup();
|
|
72210
|
-
|
|
72330
|
+
resolve17();
|
|
72211
72331
|
}, ms);
|
|
72212
72332
|
const onAbort = () => {
|
|
72213
72333
|
cleanup();
|
|
72214
|
-
|
|
72334
|
+
resolve17();
|
|
72215
72335
|
};
|
|
72216
72336
|
const cleanup = () => {
|
|
72217
72337
|
clearTimeout(timeout);
|
|
@@ -72972,10 +73092,10 @@ var init_tunnel_process_manager = __esm({
|
|
|
72972
73092
|
lastError: null
|
|
72973
73093
|
});
|
|
72974
73094
|
this.emitLog("info", "manager", `Stopping ${currentHandle.provider} tunnel (pid=${currentHandle.child.pid ?? "n/a"})`);
|
|
72975
|
-
this.activeStopPromise = new Promise((
|
|
73095
|
+
this.activeStopPromise = new Promise((resolve17) => {
|
|
72976
73096
|
const onClose = () => {
|
|
72977
73097
|
currentHandle.child.removeListener("close", onClose);
|
|
72978
|
-
|
|
73098
|
+
resolve17();
|
|
72979
73099
|
};
|
|
72980
73100
|
currentHandle.child.once("close", onClose);
|
|
72981
73101
|
killManagedProcess(currentHandle.child, "SIGTERM");
|
|
@@ -73154,6 +73274,12 @@ var init_project_engine = __esm({
|
|
|
73154
73274
|
manualMergeResolvers = /* @__PURE__ */ new Map();
|
|
73155
73275
|
shuttingDown = false;
|
|
73156
73276
|
static MAX_AUTO_MERGE_RETRIES = 3;
|
|
73277
|
+
/** Cap on outer in-review→in-progress bounces caused by deterministic
|
|
73278
|
+
* verification failures during auto-merge. After this many failed merges
|
|
73279
|
+
* for the same task, we stop bouncing it back, mark it failed, and create
|
|
73280
|
+
* a follow-up triage task so a fresh agent (or human) can investigate
|
|
73281
|
+
* the underlying flake/regression instead of looping forever. */
|
|
73282
|
+
static MAX_VERIFICATION_FAILURE_BOUNCES = 3;
|
|
73157
73283
|
/** 30-minute cooldown before a retry-exhausted task gets another sweep attempt */
|
|
73158
73284
|
static AUTO_MERGE_COOLDOWN_MS = 30 * 60 * 1e3;
|
|
73159
73285
|
// Event handler references for cleanup
|
|
@@ -73438,12 +73564,12 @@ var init_project_engine = __esm({
|
|
|
73438
73564
|
*/
|
|
73439
73565
|
async onMerge(taskId) {
|
|
73440
73566
|
if (this.mergeActive.has(taskId)) {
|
|
73441
|
-
return new Promise((
|
|
73442
|
-
this.manualMergeResolvers.set(taskId, { resolve:
|
|
73567
|
+
return new Promise((resolve17, reject) => {
|
|
73568
|
+
this.manualMergeResolvers.set(taskId, { resolve: resolve17, reject });
|
|
73443
73569
|
});
|
|
73444
73570
|
}
|
|
73445
|
-
return new Promise((
|
|
73446
|
-
this.manualMergeResolvers.set(taskId, { resolve:
|
|
73571
|
+
return new Promise((resolve17, reject) => {
|
|
73572
|
+
this.manualMergeResolvers.set(taskId, { resolve: resolve17, reject });
|
|
73447
73573
|
this.internalEnqueueMerge(taskId);
|
|
73448
73574
|
});
|
|
73449
73575
|
}
|
|
@@ -73830,20 +73956,61 @@ var init_project_engine = __esm({
|
|
|
73830
73956
|
const isVerificationError = err instanceof Error && err.name === "VerificationError" || errorMsg.includes("Deterministic test verification failed") || errorMsg.includes("Deterministic build verification failed");
|
|
73831
73957
|
if (taskOnErr && isVerificationError) {
|
|
73832
73958
|
const failedKind = errorMsg.includes("build verification") ? "build" : "test";
|
|
73959
|
+
const previousBounces = taskOnErr.verificationFailureCount ?? 0;
|
|
73960
|
+
const nextBounces = previousBounces + 1;
|
|
73961
|
+
const cap = _ProjectEngine.MAX_VERIFICATION_FAILURE_BOUNCES;
|
|
73962
|
+
if (nextBounces >= cap) {
|
|
73963
|
+
try {
|
|
73964
|
+
await store.updateTask(taskId, {
|
|
73965
|
+
status: "failed",
|
|
73966
|
+
verificationFailureCount: nextBounces,
|
|
73967
|
+
error: `Deterministic ${failedKind} verification failed ${nextBounces}\xD7 \u2014 auto-merge giving up to avoid infinite retry loop. See follow-up task for investigation.`
|
|
73968
|
+
});
|
|
73969
|
+
const followUpDescription = `Investigate repeated ${failedKind} verification failure on ${taskId} (${taskOnErr.title || "untitled"}). Auto-merge attempted to fix and re-verify ${nextBounces} times without success \u2014 likely a flaky test or unrelated regression rather than a fix this task can produce on its own. Look at the most recent [verification] log entries on ${taskId} for the failing command and output, then either fix the underlying issue or quarantine the flake.`;
|
|
73970
|
+
const followUp = await store.createTask({
|
|
73971
|
+
description: followUpDescription,
|
|
73972
|
+
column: "triage",
|
|
73973
|
+
priority: "high"
|
|
73974
|
+
});
|
|
73975
|
+
await store.addTaskComment(
|
|
73976
|
+
taskId,
|
|
73977
|
+
`Auto-merge giving up after ${nextBounces} verification-failure bounces. Created follow-up ${followUp.id} to investigate.`,
|
|
73978
|
+
"agent"
|
|
73979
|
+
);
|
|
73980
|
+
await store.logEntry(
|
|
73981
|
+
taskId,
|
|
73982
|
+
`Auto-merge gave up after ${nextBounces} verification-failure bounces \u2014 created follow-up ${followUp.id}`,
|
|
73983
|
+
"VerificationError"
|
|
73984
|
+
);
|
|
73985
|
+
runtimeLog.warn(
|
|
73986
|
+
`Auto-merge: ${taskId} hit verification-failure cap (${nextBounces}/${cap}) \u2014 failed task and created follow-up ${followUp.id}`
|
|
73987
|
+
);
|
|
73988
|
+
} catch (followUpErr) {
|
|
73989
|
+
runtimeLog.error(
|
|
73990
|
+
`Auto-merge: failed to fail-and-followup ${taskId} after verification cap: ${followUpErr instanceof Error ? followUpErr.message : String(followUpErr)}`
|
|
73991
|
+
);
|
|
73992
|
+
}
|
|
73993
|
+
continue;
|
|
73994
|
+
}
|
|
73833
73995
|
try {
|
|
73834
73996
|
await store.addTaskComment(
|
|
73835
73997
|
taskId,
|
|
73836
|
-
`Deterministic ${failedKind} verification failed during merge. See the prior [verification] log entry for the truncated command output. Please fix the failing ${failedKind} and push the update so the merge can retry.`,
|
|
73998
|
+
`Deterministic ${failedKind} verification failed during merge (attempt ${nextBounces}/${cap}). See the prior [verification] log entry for the truncated command output. Please fix the failing ${failedKind} and push the update so the merge can retry.`,
|
|
73837
73999
|
"agent"
|
|
73838
74000
|
);
|
|
73839
|
-
await store.updateTask(taskId, {
|
|
74001
|
+
await store.updateTask(taskId, {
|
|
74002
|
+
status: null,
|
|
74003
|
+
mergeRetries: 0,
|
|
74004
|
+
error: null,
|
|
74005
|
+
verificationFailureCount: nextBounces
|
|
74006
|
+
});
|
|
73840
74007
|
await store.moveTask(taskId, "in-progress");
|
|
73841
74008
|
await store.logEntry(
|
|
73842
74009
|
taskId,
|
|
73843
|
-
`Deterministic ${failedKind} verification failed \u2014 moved back to in-progress for remediation`
|
|
74010
|
+
`Deterministic ${failedKind} verification failed (${nextBounces}/${cap}) \u2014 moved back to in-progress for remediation`
|
|
73844
74011
|
);
|
|
73845
74012
|
runtimeLog.log(
|
|
73846
|
-
`Auto-merge: ${taskId} deterministic ${failedKind} verification failed \u2014 moved to in-progress`
|
|
74013
|
+
`Auto-merge: ${taskId} deterministic ${failedKind} verification failed (${nextBounces}/${cap}) \u2014 moved to in-progress`
|
|
73847
74014
|
);
|
|
73848
74015
|
} catch {
|
|
73849
74016
|
runtimeLog.error(
|
|
@@ -74057,6 +74224,11 @@ var init_project_engine = __esm({
|
|
|
74057
74224
|
wireSettingsListeners(store) {
|
|
74058
74225
|
const onGlobalPause = ({ settings, previous }) => {
|
|
74059
74226
|
if (settings.globalPause && !previous.globalPause) {
|
|
74227
|
+
if (this.mergeAbortController) {
|
|
74228
|
+
runtimeLog.log("Global pause \u2014 aborting in-flight merge verification");
|
|
74229
|
+
this.mergeAbortController.abort();
|
|
74230
|
+
this.mergeAbortController = null;
|
|
74231
|
+
}
|
|
74060
74232
|
if (this.activeMergeSession) {
|
|
74061
74233
|
runtimeLog.log("Global pause \u2014 terminating active merge session");
|
|
74062
74234
|
this.activeMergeSession.dispose();
|
|
@@ -75099,7 +75271,6 @@ __export(src_exports2, {
|
|
|
75099
75271
|
taskLogParams: () => taskLogParams,
|
|
75100
75272
|
withRateLimitRetry: () => withRateLimitRetry
|
|
75101
75273
|
});
|
|
75102
|
-
var fusionCoreExports;
|
|
75103
75274
|
var init_src2 = __esm({
|
|
75104
75275
|
"../engine/src/index.ts"() {
|
|
75105
75276
|
"use strict";
|
|
@@ -75114,7 +75285,6 @@ var init_src2 = __esm({
|
|
|
75114
75285
|
init_merger();
|
|
75115
75286
|
init_reviewer();
|
|
75116
75287
|
init_pi();
|
|
75117
|
-
init_src();
|
|
75118
75288
|
init_pi();
|
|
75119
75289
|
init_skill_resolver();
|
|
75120
75290
|
init_agent_reflection();
|
|
@@ -75145,12 +75315,12 @@ var init_src2 = __esm({
|
|
|
75145
75315
|
init_remote_node_client();
|
|
75146
75316
|
init_remote_node_runtime();
|
|
75147
75317
|
init_step_session_executor();
|
|
75148
|
-
|
|
75149
|
-
|
|
75150
|
-
|
|
75151
|
-
|
|
75152
|
-
|
|
75153
|
-
}
|
|
75318
|
+
void Promise.resolve().then(() => (init_src(), src_exports)).then((core) => {
|
|
75319
|
+
if ("setCreateFnAgent" in core && typeof core.setCreateFnAgent === "function") {
|
|
75320
|
+
core.setCreateFnAgent(createFnAgent2);
|
|
75321
|
+
}
|
|
75322
|
+
}).catch(() => {
|
|
75323
|
+
});
|
|
75154
75324
|
}
|
|
75155
75325
|
});
|
|
75156
75326
|
|
|
@@ -75302,17 +75472,6 @@ var init_ai_session_diagnostics = __esm({
|
|
|
75302
75472
|
// ../dashboard/src/planning.ts
|
|
75303
75473
|
import { randomUUID as randomUUID10 } from "node:crypto";
|
|
75304
75474
|
import { EventEmitter as EventEmitter23 } from "node:events";
|
|
75305
|
-
function buildDefaultPlanningNtfyHelpers() {
|
|
75306
|
-
const isNtfyEventEnabled2 = "isNtfyEventEnabled" in src_exports2 ? isNtfyEventEnabled : (events, event) => Array.isArray(events) ? events.includes(event) : false;
|
|
75307
|
-
const buildNtfyClickUrl2 = "buildNtfyClickUrl" in src_exports2 ? buildNtfyClickUrl : () => void 0;
|
|
75308
|
-
const sendNtfyNotification2 = "sendNtfyNotification" in src_exports2 ? sendNtfyNotification : async () => {
|
|
75309
|
-
};
|
|
75310
|
-
return {
|
|
75311
|
-
isNtfyEventEnabled: isNtfyEventEnabled2,
|
|
75312
|
-
buildNtfyClickUrl: buildNtfyClickUrl2,
|
|
75313
|
-
sendNtfyNotification: sendNtfyNotification2
|
|
75314
|
-
};
|
|
75315
|
-
}
|
|
75316
75475
|
function ensureEngineReady() {
|
|
75317
75476
|
return Promise.resolve();
|
|
75318
75477
|
}
|
|
@@ -76060,7 +76219,7 @@ function getSession(sessionId) {
|
|
|
76060
76219
|
return void 0;
|
|
76061
76220
|
}
|
|
76062
76221
|
}
|
|
76063
|
-
var createFnAgent4,
|
|
76222
|
+
var createFnAgent4, planningNtfyHelpers, diagnostics, PLANNING_SYSTEM_PROMPT, SESSION_TTL_MS, CLEANUP_INTERVAL_MS2, MAX_SESSIONS_PER_IP_PER_HOUR, RATE_LIMIT_WINDOW_MS2, sessions, rateLimits2, _aiSessionStore, cleanupInterval2, PlanningStreamManager, planningStreamManager, MAX_PARSE_RETRIES, RateLimitError2, SessionNotFoundError, InvalidSessionStateError;
|
|
76064
76223
|
var init_planning = __esm({
|
|
76065
76224
|
"../dashboard/src/planning.ts"() {
|
|
76066
76225
|
"use strict";
|
|
@@ -76069,12 +76228,6 @@ var init_planning = __esm({
|
|
|
76069
76228
|
init_ai_session_diagnostics();
|
|
76070
76229
|
init_src2();
|
|
76071
76230
|
createFnAgent4 = createFnAgent2;
|
|
76072
|
-
engineExports = src_exports2;
|
|
76073
|
-
engineIsNtfyEventEnabled = "isNtfyEventEnabled" in engineExports && typeof engineExports["isNtfyEventEnabled"] === "function" ? engineExports["isNtfyEventEnabled"] : (events, event) => Array.isArray(events) && events.includes(event);
|
|
76074
|
-
engineBuildNtfyClickUrl = "buildNtfyClickUrl" in engineExports && typeof engineExports["buildNtfyClickUrl"] === "function" ? engineExports["buildNtfyClickUrl"] : (_options) => void 0;
|
|
76075
|
-
engineSendNtfyNotification = "sendNtfyNotification" in engineExports && typeof engineExports["sendNtfyNotification"] === "function" ? engineExports["sendNtfyNotification"] : async (_input) => {
|
|
76076
|
-
};
|
|
76077
|
-
planningNtfyHelpers = buildDefaultPlanningNtfyHelpers();
|
|
76078
76231
|
diagnostics = createSessionDiagnostics("planning");
|
|
76079
76232
|
PLANNING_SYSTEM_PROMPT = `You are a planning assistant for the fn task board system.
|
|
76080
76233
|
|
|
@@ -76728,7 +76881,7 @@ var init_register_messaging_scripts = __esm({
|
|
|
76728
76881
|
|
|
76729
76882
|
// ../dashboard/src/github.ts
|
|
76730
76883
|
function delay(ms) {
|
|
76731
|
-
return new Promise((
|
|
76884
|
+
return new Promise((resolve17) => setTimeout(resolve17, ms));
|
|
76732
76885
|
}
|
|
76733
76886
|
function normalizeCheckState(state) {
|
|
76734
76887
|
switch ((state ?? "").toLowerCase()) {
|
|
@@ -77444,6 +77597,43 @@ var init_github = __esm({
|
|
|
77444
77597
|
}
|
|
77445
77598
|
return response.json();
|
|
77446
77599
|
}
|
|
77600
|
+
async commentOnIssue(owner, repo, issueNumber, body) {
|
|
77601
|
+
if (this.hasGhAuth()) {
|
|
77602
|
+
try {
|
|
77603
|
+
runGh([
|
|
77604
|
+
"issue",
|
|
77605
|
+
"comment",
|
|
77606
|
+
String(issueNumber),
|
|
77607
|
+
"--repo",
|
|
77608
|
+
`${owner}/${repo}`,
|
|
77609
|
+
"--body",
|
|
77610
|
+
body
|
|
77611
|
+
]);
|
|
77612
|
+
return;
|
|
77613
|
+
} catch (err) {
|
|
77614
|
+
if (!this.token) {
|
|
77615
|
+
throw new Error(getGhErrorMessage(err));
|
|
77616
|
+
}
|
|
77617
|
+
}
|
|
77618
|
+
}
|
|
77619
|
+
if (!this.token) {
|
|
77620
|
+
throw new Error("GitHub CLI (gh) is not available or not authenticated, and no GITHUB_TOKEN provided.");
|
|
77621
|
+
}
|
|
77622
|
+
const url = `${this.baseUrl}/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}/issues/${issueNumber}/comments`;
|
|
77623
|
+
const result = await this.fetchThrottled(
|
|
77624
|
+
url,
|
|
77625
|
+
{
|
|
77626
|
+
method: "POST",
|
|
77627
|
+
headers: {
|
|
77628
|
+
"Content-Type": "application/json"
|
|
77629
|
+
},
|
|
77630
|
+
body: JSON.stringify({ body })
|
|
77631
|
+
}
|
|
77632
|
+
);
|
|
77633
|
+
if (!result.success) {
|
|
77634
|
+
throw new Error(result.error ?? "Failed to comment on GitHub issue");
|
|
77635
|
+
}
|
|
77636
|
+
}
|
|
77447
77637
|
/**
|
|
77448
77638
|
* Fetch current issue status using gh CLI if available, otherwise REST API.
|
|
77449
77639
|
* Returns null if the issue is not found or is a pull request.
|
|
@@ -78205,6 +78395,14 @@ var init_github = __esm({
|
|
|
78205
78395
|
}
|
|
78206
78396
|
});
|
|
78207
78397
|
|
|
78398
|
+
// ../dashboard/src/github-issue-comment.ts
|
|
78399
|
+
var init_github_issue_comment = __esm({
|
|
78400
|
+
"../dashboard/src/github-issue-comment.ts"() {
|
|
78401
|
+
"use strict";
|
|
78402
|
+
init_github();
|
|
78403
|
+
}
|
|
78404
|
+
});
|
|
78405
|
+
|
|
78208
78406
|
// ../dashboard/src/github-poll.ts
|
|
78209
78407
|
import { EventEmitter as EventEmitter27 } from "node:events";
|
|
78210
78408
|
function toAlias(type, number) {
|
|
@@ -78514,6 +78712,7 @@ var init_register_git_github = __esm({
|
|
|
78514
78712
|
init_src();
|
|
78515
78713
|
init_api_error();
|
|
78516
78714
|
init_github();
|
|
78715
|
+
init_github_issue_comment();
|
|
78517
78716
|
init_github_poll();
|
|
78518
78717
|
init_github_webhooks();
|
|
78519
78718
|
init_resolve_diff_base();
|
|
@@ -82963,15 +83162,13 @@ var init_terminal_websocket_diagnostics = __esm({
|
|
|
82963
83162
|
|
|
82964
83163
|
// ../dashboard/src/chat.ts
|
|
82965
83164
|
import { EventEmitter as EventEmitter29 } from "node:events";
|
|
82966
|
-
var
|
|
83165
|
+
var defaultDiagnostics, _diagnostics, diagnostics7, RATE_LIMIT_WINDOW_MS6, MAX_REFERENCED_FILE_SIZE, ChatStreamManager, chatStreamManager;
|
|
82967
83166
|
var init_chat = __esm({
|
|
82968
83167
|
"../dashboard/src/chat.ts"() {
|
|
82969
83168
|
"use strict";
|
|
82970
83169
|
init_src();
|
|
82971
83170
|
init_sse_buffer();
|
|
82972
83171
|
init_src2();
|
|
82973
|
-
engineExports2 = src_exports2;
|
|
82974
|
-
defaultBuildAgentChatPromptFn = "buildAgentChatPrompt" in engineExports2 && typeof engineExports2["buildAgentChatPrompt"] === "function" ? engineExports2["buildAgentChatPrompt"] : void 0;
|
|
82975
83172
|
defaultDiagnostics = {
|
|
82976
83173
|
log(message, ...args) {
|
|
82977
83174
|
console.log(`[chat] ${message}`, ...args);
|
|
@@ -83208,6 +83405,7 @@ var init_src3 = __esm({
|
|
|
83208
83405
|
init_github();
|
|
83209
83406
|
init_rate_limit();
|
|
83210
83407
|
init_github_poll();
|
|
83408
|
+
init_github_issue_comment();
|
|
83211
83409
|
init_api_error();
|
|
83212
83410
|
init_badge_pubsub();
|
|
83213
83411
|
init_plugins();
|
|
@@ -83215,7 +83413,7 @@ var init_src3 = __esm({
|
|
|
83215
83413
|
});
|
|
83216
83414
|
|
|
83217
83415
|
// src/project-context.ts
|
|
83218
|
-
import { resolve as
|
|
83416
|
+
import { resolve as resolve15, dirname as dirname10 } from "node:path";
|
|
83219
83417
|
import { existsSync as existsSync29 } from "node:fs";
|
|
83220
83418
|
async function resolveProject(projectNameFlag, cwd = process.cwd(), globalDir) {
|
|
83221
83419
|
const central = new CentralCore(globalDir);
|
|
@@ -83292,9 +83490,9 @@ async function clearDefaultProject(globalDir) {
|
|
|
83292
83490
|
await globalStore.updateSettings(rest);
|
|
83293
83491
|
}
|
|
83294
83492
|
async function detectProjectFromCwd(cwd, central) {
|
|
83295
|
-
let currentDir =
|
|
83493
|
+
let currentDir = resolve15(cwd);
|
|
83296
83494
|
while (true) {
|
|
83297
|
-
const kbPath =
|
|
83495
|
+
const kbPath = resolve15(currentDir, ".fusion", "fusion.db");
|
|
83298
83496
|
if (existsSync29(kbPath)) {
|
|
83299
83497
|
const project = await central.getProjectByPath(currentDir);
|
|
83300
83498
|
if (project) {
|
|
@@ -83468,9 +83666,9 @@ async function runTaskCreate(descriptionArg, attachFiles, depends, projectName)
|
|
|
83468
83666
|
console.log(` Path: .fusion/tasks/${task.id}/`);
|
|
83469
83667
|
if (attachFiles && attachFiles.length > 0) {
|
|
83470
83668
|
const { readFile: readFile19 } = await import("node:fs/promises");
|
|
83471
|
-
const { basename: basename9, extname: extname3, resolve:
|
|
83669
|
+
const { basename: basename9, extname: extname3, resolve: resolve17 } = await import("node:path");
|
|
83472
83670
|
for (const filePath of attachFiles) {
|
|
83473
|
-
const resolvedPath =
|
|
83671
|
+
const resolvedPath = resolve17(filePath);
|
|
83474
83672
|
const filename = basename9(resolvedPath);
|
|
83475
83673
|
const ext = extname3(filename).toLowerCase();
|
|
83476
83674
|
const mimeType = MIME_TYPES[ext];
|
|
@@ -83717,8 +83915,8 @@ async function runTaskMerge(id, projectName) {
|
|
|
83717
83915
|
async function runTaskAttach(id, filePath, projectName) {
|
|
83718
83916
|
const { readFile: readFile19 } = await import("node:fs/promises");
|
|
83719
83917
|
const { basename: basename9, extname: extname3 } = await import("node:path");
|
|
83720
|
-
const { resolve:
|
|
83721
|
-
const resolvedPath =
|
|
83918
|
+
const { resolve: resolve17 } = await import("node:path");
|
|
83919
|
+
const resolvedPath = resolve17(filePath);
|
|
83722
83920
|
const filename = basename9(resolvedPath);
|
|
83723
83921
|
const ext = extname3(filename).toLowerCase();
|
|
83724
83922
|
const mimeType = MIME_TYPES[ext];
|
|
@@ -84235,12 +84433,12 @@ async function promptText(question) {
|
|
|
84235
84433
|
console.log(" (Enter your response. Type DONE on its own line when finished):\n");
|
|
84236
84434
|
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
84237
84435
|
const lines = [];
|
|
84238
|
-
return new Promise((
|
|
84436
|
+
return new Promise((resolve17) => {
|
|
84239
84437
|
const askLine = () => {
|
|
84240
84438
|
rl.question(" ").then((line) => {
|
|
84241
84439
|
if (line.trim() === "DONE") {
|
|
84242
84440
|
rl.close();
|
|
84243
|
-
|
|
84441
|
+
resolve17(lines.join("\n"));
|
|
84244
84442
|
} else {
|
|
84245
84443
|
lines.push(line);
|
|
84246
84444
|
askLine();
|
|
@@ -84642,9 +84840,9 @@ async function runSkillsInstall(args, options) {
|
|
|
84642
84840
|
cwd: process.cwd(),
|
|
84643
84841
|
stdio: "inherit"
|
|
84644
84842
|
});
|
|
84645
|
-
const exitCode = await new Promise((
|
|
84843
|
+
const exitCode = await new Promise((resolve17, reject) => {
|
|
84646
84844
|
child.on("exit", (code) => {
|
|
84647
|
-
|
|
84845
|
+
resolve17(code ?? 1);
|
|
84648
84846
|
});
|
|
84649
84847
|
child.on("error", (err) => {
|
|
84650
84848
|
reject(err);
|
|
@@ -84671,7 +84869,7 @@ init_src();
|
|
|
84671
84869
|
init_gh_cli();
|
|
84672
84870
|
import { Type as Type7 } from "typebox";
|
|
84673
84871
|
import { StringEnum } from "@mariozechner/pi-ai";
|
|
84674
|
-
import { resolve as
|
|
84872
|
+
import { resolve as resolve16, basename as basename8, extname as extname2, join as join37 } from "node:path";
|
|
84675
84873
|
import { readFile as readFile18 } from "node:fs/promises";
|
|
84676
84874
|
import { existsSync as existsSync31 } from "node:fs";
|
|
84677
84875
|
import { spawn as spawn6 } from "node:child_process";
|
|
@@ -84691,14 +84889,14 @@ var MIME_TYPES2 = {
|
|
|
84691
84889
|
".xml": "application/xml"
|
|
84692
84890
|
};
|
|
84693
84891
|
function resolveProjectRoot(cwd) {
|
|
84694
|
-
let current =
|
|
84892
|
+
let current = resolve16(cwd);
|
|
84695
84893
|
while (true) {
|
|
84696
84894
|
if (existsSync31(join37(current, ".fusion"))) {
|
|
84697
84895
|
return current;
|
|
84698
84896
|
}
|
|
84699
|
-
const parent =
|
|
84897
|
+
const parent = resolve16(current, "..");
|
|
84700
84898
|
if (parent === current) {
|
|
84701
|
-
return
|
|
84899
|
+
return resolve16(cwd);
|
|
84702
84900
|
}
|
|
84703
84901
|
current = parent;
|
|
84704
84902
|
}
|
|
@@ -84991,7 +85189,7 @@ Column: triage
|
|
|
84991
85189
|
path: Type7.String({ description: "Path to the file to attach" })
|
|
84992
85190
|
}),
|
|
84993
85191
|
async execute(_toolCallId, params, _signal, _onUpdate, ctx) {
|
|
84994
|
-
const filePath =
|
|
85192
|
+
const filePath = resolve16(ctx.cwd, params.path.replace(/^@/, ""));
|
|
84995
85193
|
const filename = basename8(filePath);
|
|
84996
85194
|
const ext = extname2(filename).toLowerCase();
|
|
84997
85195
|
const mimeType = MIME_TYPES2[ext];
|
|
@@ -86085,12 +86283,12 @@ Status: ${updated.status}`
|
|
|
86085
86283
|
child.stderr?.on("data", (data) => {
|
|
86086
86284
|
stderr += data.toString();
|
|
86087
86285
|
});
|
|
86088
|
-
const exitCode = await new Promise((
|
|
86286
|
+
const exitCode = await new Promise((resolve17) => {
|
|
86089
86287
|
child.on("exit", (code) => {
|
|
86090
|
-
|
|
86288
|
+
resolve17(code ?? 1);
|
|
86091
86289
|
});
|
|
86092
86290
|
child.on("error", () => {
|
|
86093
|
-
|
|
86291
|
+
resolve17(1);
|
|
86094
86292
|
});
|
|
86095
86293
|
});
|
|
86096
86294
|
try {
|