@runfusion/fusion 0.37.0 → 0.38.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 +1510 -234
- package/dist/client/.vite/manifest.json +58 -58
- package/dist/client/assets/AgentDetailView-CquLtIia.js +18 -0
- package/dist/client/assets/{AgentPermissionPolicyEditor-niLSqGAE.js → AgentPermissionPolicyEditor-Dk-oEs1X.js} +1 -1
- package/dist/client/assets/AgentsView-BZVOpQ05.js +29 -0
- package/dist/client/assets/ChatView-CErtz86Y.js +6 -0
- package/dist/client/assets/{DevServerView-0Gb44WOb.js → DevServerView-BRA00XoT.js} +1 -1
- package/dist/client/assets/{DirectoryPicker-BSQA0XM9.js → DirectoryPicker-D496NTjZ.js} +1 -1
- package/dist/client/assets/{DocumentsView-XZbaqEFF.js → DocumentsView-DIRlgdeB.js} +1 -1
- package/dist/client/assets/{EvalsView-CJNtXxX9.js → EvalsView-By5mtr80.js} +1 -1
- package/dist/client/assets/{ExperimentalAgentOnboardingModal-DR8E3i36.js → ExperimentalAgentOnboardingModal-Br57oNdX.js} +1 -1
- package/dist/client/assets/GoalsView-B65urz9k.css +1 -0
- package/dist/client/assets/{GoalsView-CN1zQ_Ax.js → GoalsView-COI14CWJ.js} +1 -1
- package/dist/client/assets/{InsightsView-D_jRaMyn.js → InsightsView-Hm7vX2lr.js} +2 -2
- package/dist/client/assets/{MemoryView-D90vdPnp.js → MemoryView-BLozyYur.js} +1 -1
- package/dist/client/assets/{NodesView-C1zuedAe.js → NodesView-bXC6WoE2.js} +1 -1
- package/dist/client/assets/{PiExtensionsManager-DBJDDVfM.js → PiExtensionsManager-DEiqcMkx.js} +2 -2
- package/dist/client/assets/{PluginManager-Ceg64P2Z.js → PluginManager-l4224zxk.js} +1 -1
- package/dist/client/assets/{ResearchView-XRwWdczc.js → ResearchView-CpsR1Q56.js} +1 -1
- package/dist/client/assets/{SecretsView-TCrKyCme.js → SecretsView-Bwme1zZI.js} +1 -1
- package/dist/client/assets/{SettingsModal-C1TczsEr.js → SettingsModal-32vUNWRe.js} +1 -1
- package/dist/client/assets/SettingsModal-D_uIvf6n.css +1 -0
- package/dist/client/assets/SettingsModal-DdrXxMPW.js +31 -0
- package/dist/client/assets/{SetupWizardModal-D6moSTT3.js → SetupWizardModal-BFD9E0H_.js} +1 -1
- package/dist/client/assets/{SkillsView-BDHLPVBi.js → SkillsView-CZMLY7F_.js} +1 -1
- package/dist/client/assets/{StashRecoveryView-DZGa591R.js → StashRecoveryView-jgZeWbZo.js} +1 -1
- package/dist/client/assets/{TodoView-nr_SnCJv.js → TodoView-DXdnOymj.js} +2 -2
- package/dist/client/assets/{dashboard-view-0XeZ1FTR.js → dashboard-view-H8SnaCPP.js} +2 -2
- package/dist/client/assets/{folder-open-iURwQbEg.js → folder-open-Dh5_gv6L.js} +1 -1
- package/dist/client/assets/index-B9hAW3eU.css +1 -0
- package/dist/client/assets/index-w1dPHtmq.js +742 -0
- package/dist/client/assets/{upload-Beqg4S4L.js → upload-ntU2YHPl.js} +1 -1
- package/dist/client/assets/{users-C2o86ZCR.js → users-BWK61s9U.js} +1 -1
- package/dist/client/index.html +40 -3
- package/dist/client/version.json +1 -1
- package/dist/droid-cli/package.json +1 -1
- package/dist/extension.js +1326 -182
- package/dist/pi-claude-cli/package.json +1 -1
- package/dist/plugins/fusion-plugin-cli-printing-press/package.json +1 -1
- package/dist/plugins/fusion-plugin-cursor-runtime/package.json +1 -1
- package/dist/plugins/fusion-plugin-dependency-graph/package.json +1 -1
- package/dist/plugins/fusion-plugin-droid-runtime/package.json +1 -1
- package/dist/plugins/fusion-plugin-hermes-runtime/package.json +1 -1
- package/dist/plugins/fusion-plugin-openclaw-runtime/package.json +1 -1
- package/dist/plugins/fusion-plugin-paperclip-runtime/package.json +1 -1
- package/dist/plugins/fusion-plugin-reports/package.json +1 -1
- package/dist/plugins/fusion-plugin-roadmap/package.json +1 -1
- package/dist/plugins/fusion-plugin-whatsapp-chat/package.json +1 -1
- package/package.json +1 -1
- package/dist/client/assets/AgentDetailView-T_AHPxcG.js +0 -18
- package/dist/client/assets/AgentsView-CQhaLHz_.js +0 -29
- package/dist/client/assets/ChatView-B60kYfdk.js +0 -6
- package/dist/client/assets/GoalsView-CtWi0fxE.css +0 -1
- package/dist/client/assets/SettingsModal-0gevFhXV.css +0 -1
- package/dist/client/assets/SettingsModal-B8fRuWdQ.js +0 -31
- package/dist/client/assets/index-CjuQj4qe.js +0 -742
- package/dist/client/assets/index-DmrpDn7c.css +0 -1
package/dist/extension.js
CHANGED
|
@@ -4672,7 +4672,7 @@ var init_db = __esm({
|
|
|
4672
4672
|
DEFAULT_SQLITE_BUSY_TIMEOUT_MS = 5e3;
|
|
4673
4673
|
DEFAULT_SQLITE_LOCK_RECOVERY_WINDOW_MS = 1e3;
|
|
4674
4674
|
DEFAULT_SQLITE_LOCK_RECOVERY_DELAY_MS = 50;
|
|
4675
|
-
SCHEMA_VERSION =
|
|
4675
|
+
SCHEMA_VERSION = 99;
|
|
4676
4676
|
SCHEMA_SQL = `
|
|
4677
4677
|
-- Tasks table with JSON columns for nested data
|
|
4678
4678
|
CREATE TABLE IF NOT EXISTS tasks (
|
|
@@ -4694,6 +4694,7 @@ CREATE TABLE IF NOT EXISTS tasks (
|
|
|
4694
4694
|
pausedReason TEXT,
|
|
4695
4695
|
baseBranch TEXT,
|
|
4696
4696
|
branch TEXT,
|
|
4697
|
+
autoMerge INTEGER,
|
|
4697
4698
|
executionStartBranch TEXT,
|
|
4698
4699
|
baseCommitSha TEXT,
|
|
4699
4700
|
modelPresetId TEXT,
|
|
@@ -4705,12 +4706,16 @@ CREATE TABLE IF NOT EXISTS tasks (
|
|
|
4705
4706
|
planningModelId TEXT,
|
|
4706
4707
|
mergeRetries INTEGER,
|
|
4707
4708
|
workflowStepRetries INTEGER,
|
|
4709
|
+
resumeLimboCount INTEGER DEFAULT 0,
|
|
4710
|
+
resumeLimboTipSha TEXT,
|
|
4711
|
+
resumeLimboStepSignature TEXT,
|
|
4708
4712
|
recoveryRetryCount INTEGER,
|
|
4709
4713
|
taskDoneRetryCount INTEGER DEFAULT 0,
|
|
4710
4714
|
worktreeSessionRetryCount INTEGER DEFAULT 0,
|
|
4711
4715
|
completionHandoffLimboRecoveryCount INTEGER DEFAULT 0,
|
|
4712
4716
|
mergeConflictBounceCount INTEGER DEFAULT 0,
|
|
4713
4717
|
mergeAuditBounceCount INTEGER DEFAULT 0,
|
|
4718
|
+
mergeTransientRetryCount INTEGER DEFAULT 0,
|
|
4714
4719
|
nextRecoveryAt TEXT,
|
|
4715
4720
|
error TEXT,
|
|
4716
4721
|
summary TEXT,
|
|
@@ -5220,11 +5225,31 @@ CREATE TABLE IF NOT EXISTS missions (
|
|
|
5220
5225
|
status TEXT NOT NULL,
|
|
5221
5226
|
interviewState TEXT NOT NULL,
|
|
5222
5227
|
baseBranch TEXT,
|
|
5228
|
+
branchStrategy TEXT,
|
|
5223
5229
|
autoAdvance INTEGER DEFAULT 0,
|
|
5230
|
+
autoMerge INTEGER,
|
|
5224
5231
|
createdAt TEXT NOT NULL,
|
|
5225
5232
|
updatedAt TEXT NOT NULL
|
|
5226
5233
|
);
|
|
5227
5234
|
|
|
5235
|
+
CREATE TABLE IF NOT EXISTS branch_groups (
|
|
5236
|
+
id TEXT PRIMARY KEY,
|
|
5237
|
+
sourceType TEXT NOT NULL CHECK (sourceType IN ('mission','planning')),
|
|
5238
|
+
sourceId TEXT NOT NULL,
|
|
5239
|
+
branchName TEXT NOT NULL UNIQUE,
|
|
5240
|
+
worktreePath TEXT,
|
|
5241
|
+
autoMerge INTEGER NOT NULL DEFAULT 0,
|
|
5242
|
+
prState TEXT NOT NULL DEFAULT 'none' CHECK (prState IN ('none','open','merged','closed')),
|
|
5243
|
+
prUrl TEXT,
|
|
5244
|
+
prNumber INTEGER,
|
|
5245
|
+
status TEXT NOT NULL DEFAULT 'open' CHECK (status IN ('open','finalized','abandoned')),
|
|
5246
|
+
createdAt INTEGER NOT NULL,
|
|
5247
|
+
updatedAt INTEGER NOT NULL,
|
|
5248
|
+
closedAt INTEGER
|
|
5249
|
+
);
|
|
5250
|
+
CREATE INDEX IF NOT EXISTS idxBranchGroupsSource ON branch_groups(sourceType, sourceId);
|
|
5251
|
+
CREATE INDEX IF NOT EXISTS idxBranchGroupsBranchName ON branch_groups(branchName);
|
|
5252
|
+
|
|
5228
5253
|
-- Goals table (strategic intent across mission timelines)
|
|
5229
5254
|
CREATE TABLE IF NOT EXISTS goals (
|
|
5230
5255
|
id TEXT PRIMARY KEY,
|
|
@@ -5236,6 +5261,22 @@ CREATE TABLE IF NOT EXISTS goals (
|
|
|
5236
5261
|
);
|
|
5237
5262
|
CREATE INDEX IF NOT EXISTS idxGoalsStatus ON goals(status);
|
|
5238
5263
|
|
|
5264
|
+
CREATE TABLE IF NOT EXISTS goal_citations (
|
|
5265
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
5266
|
+
goalId TEXT NOT NULL,
|
|
5267
|
+
agentId TEXT NOT NULL,
|
|
5268
|
+
taskId TEXT,
|
|
5269
|
+
surface TEXT NOT NULL,
|
|
5270
|
+
sourceRef TEXT NOT NULL,
|
|
5271
|
+
snippet TEXT NOT NULL,
|
|
5272
|
+
timestamp TEXT NOT NULL
|
|
5273
|
+
);
|
|
5274
|
+
CREATE INDEX IF NOT EXISTS idxGoalCitationsGoalId ON goal_citations(goalId);
|
|
5275
|
+
CREATE INDEX IF NOT EXISTS idxGoalCitationsAgentId ON goal_citations(agentId);
|
|
5276
|
+
CREATE INDEX IF NOT EXISTS idxGoalCitationsTimestamp ON goal_citations(timestamp);
|
|
5277
|
+
CREATE UNIQUE INDEX IF NOT EXISTS uxGoalCitationsDedup
|
|
5278
|
+
ON goal_citations(goalId, surface, sourceRef);
|
|
5279
|
+
|
|
5239
5280
|
-- Milestones table (phases within a mission)
|
|
5240
5281
|
CREATE TABLE IF NOT EXISTS milestones (
|
|
5241
5282
|
id TEXT PRIMARY KEY,
|
|
@@ -5532,6 +5573,7 @@ CREATE INDEX IF NOT EXISTS idxTodoItemsSortOrder ON todo_items(listId, sortOrder
|
|
|
5532
5573
|
assertion: "TEXT NOT NULL",
|
|
5533
5574
|
status: "TEXT NOT NULL DEFAULT 'pending'",
|
|
5534
5575
|
orderIndex: "INTEGER NOT NULL DEFAULT 0",
|
|
5576
|
+
sourceFeatureId: "TEXT",
|
|
5535
5577
|
createdAt: "TEXT NOT NULL",
|
|
5536
5578
|
updatedAt: "TEXT NOT NULL"
|
|
5537
5579
|
},
|
|
@@ -7335,6 +7377,11 @@ This means a caller passed a .fusion directory where a project root was expected
|
|
|
7335
7377
|
}
|
|
7336
7378
|
if (version < 75) {
|
|
7337
7379
|
this.applyMigration(75, () => {
|
|
7380
|
+
this.addColumnIfMissing("tasks", "mergeTransientRetryCount", "INTEGER DEFAULT 0");
|
|
7381
|
+
});
|
|
7382
|
+
}
|
|
7383
|
+
if (version < 76) {
|
|
7384
|
+
this.applyMigration(76, () => {
|
|
7338
7385
|
this.db.exec(`
|
|
7339
7386
|
CREATE TABLE IF NOT EXISTS experiment_sessions (
|
|
7340
7387
|
id TEXT PRIMARY KEY,
|
|
@@ -7375,38 +7422,38 @@ This means a caller passed a .fusion directory where a project root was expected
|
|
|
7375
7422
|
this.db.exec(`CREATE INDEX IF NOT EXISTS idxExperimentRecordsType ON experiment_session_records(sessionId, type)`);
|
|
7376
7423
|
});
|
|
7377
7424
|
}
|
|
7378
|
-
if (version <
|
|
7379
|
-
this.applyMigration(
|
|
7425
|
+
if (version < 77) {
|
|
7426
|
+
this.applyMigration(77, () => {
|
|
7380
7427
|
this.addColumnIfMissing("workflow_steps", "gateMode", "TEXT NOT NULL DEFAULT 'advisory'");
|
|
7381
7428
|
this.db.exec("UPDATE workflow_steps SET gateMode = 'advisory'");
|
|
7382
7429
|
});
|
|
7383
7430
|
}
|
|
7384
|
-
if (version <
|
|
7385
|
-
this.applyMigration(
|
|
7431
|
+
if (version < 78) {
|
|
7432
|
+
this.applyMigration(78, () => {
|
|
7386
7433
|
this.addColumnIfMissing("tasks", "tokenBudgetSoftAlertedAt", "TEXT");
|
|
7387
7434
|
this.addColumnIfMissing("tasks", "tokenBudgetHardAlertedAt", "TEXT");
|
|
7388
7435
|
this.addColumnIfMissing("tasks", "tokenBudgetOverride", "TEXT");
|
|
7389
7436
|
});
|
|
7390
7437
|
}
|
|
7391
|
-
if (version <
|
|
7392
|
-
this.applyMigration(
|
|
7438
|
+
if (version < 79) {
|
|
7439
|
+
this.applyMigration(79, () => {
|
|
7393
7440
|
this.addColumnIfMissing("tasks", "branchConflictRecoveryCount", "INTEGER DEFAULT 0");
|
|
7394
7441
|
this.addColumnIfMissing("tasks", "reviewerContextRetryCount", "INTEGER DEFAULT 0");
|
|
7395
7442
|
this.addColumnIfMissing("tasks", "reviewerFallbackRetryCount", "INTEGER DEFAULT 0");
|
|
7396
7443
|
});
|
|
7397
7444
|
}
|
|
7398
|
-
if (version < 79) {
|
|
7399
|
-
this.applyMigration(79, () => {
|
|
7400
|
-
this.addColumnIfMissing("tasks", "overlapBlockedBy", "TEXT");
|
|
7401
|
-
});
|
|
7402
|
-
}
|
|
7403
7445
|
if (version < 80) {
|
|
7404
7446
|
this.applyMigration(80, () => {
|
|
7405
|
-
this.addColumnIfMissing("
|
|
7447
|
+
this.addColumnIfMissing("tasks", "overlapBlockedBy", "TEXT");
|
|
7406
7448
|
});
|
|
7407
7449
|
}
|
|
7408
7450
|
if (version < 81) {
|
|
7409
7451
|
this.applyMigration(81, () => {
|
|
7452
|
+
this.addColumnIfMissing("milestones", "acceptanceCriteria", "TEXT");
|
|
7453
|
+
});
|
|
7454
|
+
}
|
|
7455
|
+
if (version < 82) {
|
|
7456
|
+
this.applyMigration(82, () => {
|
|
7410
7457
|
this.addColumnIfMissing("tasks", "firstExecutionAt", "TEXT");
|
|
7411
7458
|
this.addColumnIfMissing("tasks", "cumulativeActiveMs", "INTEGER");
|
|
7412
7459
|
if (this.hasColumn("tasks", "executionStartedAt")) {
|
|
@@ -7419,13 +7466,13 @@ This means a caller passed a .fusion directory where a project root was expected
|
|
|
7419
7466
|
}
|
|
7420
7467
|
});
|
|
7421
7468
|
}
|
|
7422
|
-
if (version <
|
|
7423
|
-
this.applyMigration(
|
|
7469
|
+
if (version < 83) {
|
|
7470
|
+
this.applyMigration(83, () => {
|
|
7424
7471
|
this.addColumnIfMissing("tasks", "worktreeSessionRetryCount", "INTEGER DEFAULT 0");
|
|
7425
7472
|
});
|
|
7426
7473
|
}
|
|
7427
|
-
if (version <
|
|
7428
|
-
this.applyMigration(
|
|
7474
|
+
if (version < 84) {
|
|
7475
|
+
this.applyMigration(84, () => {
|
|
7429
7476
|
if (!this.hasTable("secrets")) {
|
|
7430
7477
|
this.db.exec(`
|
|
7431
7478
|
CREATE TABLE secrets (
|
|
@@ -7449,8 +7496,8 @@ This means a caller passed a .fusion directory where a project root was expected
|
|
|
7449
7496
|
}
|
|
7450
7497
|
});
|
|
7451
7498
|
}
|
|
7452
|
-
if (version <
|
|
7453
|
-
this.applyMigration(
|
|
7499
|
+
if (version < 85) {
|
|
7500
|
+
this.applyMigration(85, () => {
|
|
7454
7501
|
if (!this.hasColumn("tasks", "title")) {
|
|
7455
7502
|
console.log("[title-id-drift] db.ts migration normalized 0 active titles");
|
|
7456
7503
|
return;
|
|
@@ -7472,24 +7519,24 @@ This means a caller passed a .fusion directory where a project root was expected
|
|
|
7472
7519
|
console.log(`[title-id-drift] db.ts migration normalized ${normalizedCount} active titles`);
|
|
7473
7520
|
});
|
|
7474
7521
|
}
|
|
7475
|
-
if (version < 85) {
|
|
7476
|
-
this.applyMigration(85, () => {
|
|
7477
|
-
this.addColumnIfMissing("tasks", "completionHandoffLimboRecoveryCount", "INTEGER DEFAULT 0");
|
|
7478
|
-
});
|
|
7479
|
-
}
|
|
7480
7522
|
if (version < 86) {
|
|
7481
7523
|
this.applyMigration(86, () => {
|
|
7482
|
-
this.addColumnIfMissing("tasks", "
|
|
7524
|
+
this.addColumnIfMissing("tasks", "completionHandoffLimboRecoveryCount", "INTEGER DEFAULT 0");
|
|
7483
7525
|
});
|
|
7484
7526
|
}
|
|
7485
7527
|
if (version < 87) {
|
|
7486
7528
|
this.applyMigration(87, () => {
|
|
7487
|
-
this.addColumnIfMissing("tasks", "
|
|
7488
|
-
this.db.exec("CREATE INDEX IF NOT EXISTS idx_tasks_deletedAt ON tasks(deletedAt)");
|
|
7529
|
+
this.addColumnIfMissing("tasks", "prInfos", "TEXT");
|
|
7489
7530
|
});
|
|
7490
7531
|
}
|
|
7491
7532
|
if (version < 88) {
|
|
7492
7533
|
this.applyMigration(88, () => {
|
|
7534
|
+
this.addColumnIfMissing("tasks", "deletedAt", "TEXT");
|
|
7535
|
+
this.db.exec("CREATE INDEX IF NOT EXISTS idx_tasks_deletedAt ON tasks(deletedAt)");
|
|
7536
|
+
});
|
|
7537
|
+
}
|
|
7538
|
+
if (version < 89) {
|
|
7539
|
+
this.applyMigration(89, () => {
|
|
7493
7540
|
this.addColumnIfMissing("tasks", "allowResurrection", "INTEGER DEFAULT 0");
|
|
7494
7541
|
try {
|
|
7495
7542
|
const taskColumns = this.getTableColumns("tasks");
|
|
@@ -7514,8 +7561,8 @@ This means a caller passed a .fusion directory where a project root was expected
|
|
|
7514
7561
|
}
|
|
7515
7562
|
});
|
|
7516
7563
|
}
|
|
7517
|
-
if (version <
|
|
7518
|
-
this.applyMigration(
|
|
7564
|
+
if (version < 90) {
|
|
7565
|
+
this.applyMigration(90, () => {
|
|
7519
7566
|
this.db.exec(`
|
|
7520
7567
|
CREATE TABLE IF NOT EXISTS mergeQueue (
|
|
7521
7568
|
taskId TEXT PRIMARY KEY REFERENCES tasks(id) ON DELETE CASCADE,
|
|
@@ -7538,18 +7585,18 @@ This means a caller passed a .fusion directory where a project root was expected
|
|
|
7538
7585
|
`);
|
|
7539
7586
|
});
|
|
7540
7587
|
}
|
|
7541
|
-
if (version < 90) {
|
|
7542
|
-
this.applyMigration(90, () => {
|
|
7543
|
-
this.addColumnIfMissing("tasks", "scopeAutoWiden", "TEXT DEFAULT '[]'");
|
|
7544
|
-
});
|
|
7545
|
-
}
|
|
7546
7588
|
if (version < 91) {
|
|
7547
7589
|
this.applyMigration(91, () => {
|
|
7548
|
-
this.addColumnIfMissing("
|
|
7590
|
+
this.addColumnIfMissing("tasks", "scopeAutoWiden", "TEXT DEFAULT '[]'");
|
|
7549
7591
|
});
|
|
7550
7592
|
}
|
|
7551
7593
|
if (version < 92) {
|
|
7552
7594
|
this.applyMigration(92, () => {
|
|
7595
|
+
this.addColumnIfMissing("missions", "baseBranch", "TEXT");
|
|
7596
|
+
});
|
|
7597
|
+
}
|
|
7598
|
+
if (version < 93) {
|
|
7599
|
+
this.applyMigration(93, () => {
|
|
7553
7600
|
this.db.exec(`
|
|
7554
7601
|
CREATE TABLE IF NOT EXISTS goals (
|
|
7555
7602
|
id TEXT PRIMARY KEY,
|
|
@@ -7566,6 +7613,88 @@ This means a caller passed a .fusion directory where a project root was expected
|
|
|
7566
7613
|
`);
|
|
7567
7614
|
});
|
|
7568
7615
|
}
|
|
7616
|
+
if (version < 94) {
|
|
7617
|
+
this.applyMigration(94, () => {
|
|
7618
|
+
this.db.exec(`
|
|
7619
|
+
CREATE TABLE IF NOT EXISTS goal_citations (
|
|
7620
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
7621
|
+
goalId TEXT NOT NULL,
|
|
7622
|
+
agentId TEXT NOT NULL,
|
|
7623
|
+
taskId TEXT,
|
|
7624
|
+
surface TEXT NOT NULL,
|
|
7625
|
+
sourceRef TEXT NOT NULL,
|
|
7626
|
+
snippet TEXT NOT NULL,
|
|
7627
|
+
timestamp TEXT NOT NULL
|
|
7628
|
+
)
|
|
7629
|
+
`);
|
|
7630
|
+
this.db.exec(`
|
|
7631
|
+
CREATE INDEX IF NOT EXISTS idxGoalCitationsGoalId
|
|
7632
|
+
ON goal_citations(goalId)
|
|
7633
|
+
`);
|
|
7634
|
+
this.db.exec(`
|
|
7635
|
+
CREATE INDEX IF NOT EXISTS idxGoalCitationsAgentId
|
|
7636
|
+
ON goal_citations(agentId)
|
|
7637
|
+
`);
|
|
7638
|
+
this.db.exec(`
|
|
7639
|
+
CREATE INDEX IF NOT EXISTS idxGoalCitationsTimestamp
|
|
7640
|
+
ON goal_citations(timestamp)
|
|
7641
|
+
`);
|
|
7642
|
+
this.db.exec(`
|
|
7643
|
+
CREATE UNIQUE INDEX IF NOT EXISTS uxGoalCitationsDedup
|
|
7644
|
+
ON goal_citations(goalId, surface, sourceRef)
|
|
7645
|
+
`);
|
|
7646
|
+
});
|
|
7647
|
+
}
|
|
7648
|
+
if (version < 96) {
|
|
7649
|
+
this.applyMigration(96, () => {
|
|
7650
|
+
this.db.exec(`
|
|
7651
|
+
CREATE TABLE IF NOT EXISTS branch_groups (
|
|
7652
|
+
id TEXT PRIMARY KEY,
|
|
7653
|
+
sourceType TEXT NOT NULL CHECK (sourceType IN ('mission','planning')),
|
|
7654
|
+
sourceId TEXT NOT NULL,
|
|
7655
|
+
branchName TEXT NOT NULL UNIQUE,
|
|
7656
|
+
worktreePath TEXT,
|
|
7657
|
+
autoMerge INTEGER NOT NULL DEFAULT 0,
|
|
7658
|
+
prState TEXT NOT NULL DEFAULT 'none' CHECK (prState IN ('none','open','merged','closed')),
|
|
7659
|
+
prUrl TEXT,
|
|
7660
|
+
prNumber INTEGER,
|
|
7661
|
+
status TEXT NOT NULL DEFAULT 'open' CHECK (status IN ('open','finalized','abandoned')),
|
|
7662
|
+
createdAt INTEGER NOT NULL,
|
|
7663
|
+
updatedAt INTEGER NOT NULL,
|
|
7664
|
+
closedAt INTEGER
|
|
7665
|
+
)
|
|
7666
|
+
`);
|
|
7667
|
+
this.db.exec(`
|
|
7668
|
+
CREATE INDEX IF NOT EXISTS idxBranchGroupsSource
|
|
7669
|
+
ON branch_groups(sourceType, sourceId)
|
|
7670
|
+
`);
|
|
7671
|
+
this.db.exec(`
|
|
7672
|
+
CREATE INDEX IF NOT EXISTS idxBranchGroupsBranchName
|
|
7673
|
+
ON branch_groups(branchName)
|
|
7674
|
+
`);
|
|
7675
|
+
this.addColumnIfMissing("tasks", "autoMerge", "INTEGER");
|
|
7676
|
+
this.addColumnIfMissing("missions", "autoMerge", "INTEGER");
|
|
7677
|
+
});
|
|
7678
|
+
}
|
|
7679
|
+
if (version < 97) {
|
|
7680
|
+
this.applyMigration(97, () => {
|
|
7681
|
+
if (this.hasTable("mission_contract_assertions")) {
|
|
7682
|
+
this.addColumnIfMissing("mission_contract_assertions", "sourceFeatureId", "TEXT");
|
|
7683
|
+
}
|
|
7684
|
+
});
|
|
7685
|
+
}
|
|
7686
|
+
if (version < 98) {
|
|
7687
|
+
this.applyMigration(98, () => {
|
|
7688
|
+
this.addColumnIfMissing("missions", "branchStrategy", "TEXT");
|
|
7689
|
+
});
|
|
7690
|
+
}
|
|
7691
|
+
if (version < 99) {
|
|
7692
|
+
this.applyMigration(99, () => {
|
|
7693
|
+
this.addColumnIfMissing("tasks", "resumeLimboCount", "INTEGER DEFAULT 0");
|
|
7694
|
+
this.addColumnIfMissing("tasks", "resumeLimboTipSha", "TEXT");
|
|
7695
|
+
this.addColumnIfMissing("tasks", "resumeLimboStepSignature", "TEXT");
|
|
7696
|
+
});
|
|
7697
|
+
}
|
|
7569
7698
|
}
|
|
7570
7699
|
/**
|
|
7571
7700
|
* Run a single migration step inside a transaction and bump the version.
|
|
@@ -12302,6 +12431,53 @@ var init_db_migrate = __esm({
|
|
|
12302
12431
|
}
|
|
12303
12432
|
});
|
|
12304
12433
|
|
|
12434
|
+
// ../core/src/goal-citation-extractor.ts
|
|
12435
|
+
function extractGoalCitations(text) {
|
|
12436
|
+
const normalized = String(text ?? "");
|
|
12437
|
+
if (normalized.length === 0) {
|
|
12438
|
+
return [];
|
|
12439
|
+
}
|
|
12440
|
+
const matches = [];
|
|
12441
|
+
const seen = /* @__PURE__ */ new Set();
|
|
12442
|
+
const pattern = new RegExp(GOAL_ID_PATTERN.source, GOAL_ID_PATTERN.flags);
|
|
12443
|
+
let match;
|
|
12444
|
+
while ((match = pattern.exec(normalized)) !== null) {
|
|
12445
|
+
const goalId = match[0];
|
|
12446
|
+
if (seen.has(goalId)) {
|
|
12447
|
+
continue;
|
|
12448
|
+
}
|
|
12449
|
+
seen.add(goalId);
|
|
12450
|
+
matches.push({ goalId, index: match.index });
|
|
12451
|
+
}
|
|
12452
|
+
return matches;
|
|
12453
|
+
}
|
|
12454
|
+
function buildSnippet(text, index, max = GOAL_CITATION_SNIPPET_MAX) {
|
|
12455
|
+
const normalized = String(text ?? "");
|
|
12456
|
+
if (normalized.length === 0 || max <= 0) {
|
|
12457
|
+
return "";
|
|
12458
|
+
}
|
|
12459
|
+
const goalMatch = normalized.slice(Math.max(0, index)).match(/^G-[0-9A-Z]+(?:-[0-9A-Z]+)*/);
|
|
12460
|
+
const goalEnd = goalMatch ? index + goalMatch[0].length : index;
|
|
12461
|
+
let start = Math.max(0, index - Math.floor(max / 2));
|
|
12462
|
+
let end = Math.min(normalized.length, start + max);
|
|
12463
|
+
if (end < goalEnd) {
|
|
12464
|
+
end = Math.min(normalized.length, goalEnd);
|
|
12465
|
+
start = Math.max(0, end - max);
|
|
12466
|
+
} else {
|
|
12467
|
+
start = Math.max(0, end - max);
|
|
12468
|
+
}
|
|
12469
|
+
const snippet = normalized.slice(start, end).replace(/\s+/g, " ").trim();
|
|
12470
|
+
return snippet.length <= max ? snippet : snippet.slice(0, max).trim();
|
|
12471
|
+
}
|
|
12472
|
+
var GOAL_ID_PATTERN, GOAL_CITATION_SNIPPET_MAX;
|
|
12473
|
+
var init_goal_citation_extractor = __esm({
|
|
12474
|
+
"../core/src/goal-citation-extractor.ts"() {
|
|
12475
|
+
"use strict";
|
|
12476
|
+
GOAL_ID_PATTERN = /\bG-[0-9A-Z]+(?:-[0-9A-Z]+)*\b/g;
|
|
12477
|
+
GOAL_CITATION_SNIPPET_MAX = 200;
|
|
12478
|
+
}
|
|
12479
|
+
});
|
|
12480
|
+
|
|
12305
12481
|
// ../core/src/duplicate-detection.ts
|
|
12306
12482
|
import { createHash as createHash4 } from "node:crypto";
|
|
12307
12483
|
function normalizeFingerprintPart(value) {
|
|
@@ -12558,6 +12734,18 @@ var init_duplicate_guard = __esm({
|
|
|
12558
12734
|
|
|
12559
12735
|
// ../core/src/mission-store.ts
|
|
12560
12736
|
import { EventEmitter as EventEmitter4 } from "node:events";
|
|
12737
|
+
function missionBranchStrategyDefaults(strategy) {
|
|
12738
|
+
if (!strategy) {
|
|
12739
|
+
return { assignmentMode: "shared" };
|
|
12740
|
+
}
|
|
12741
|
+
if (strategy.mode === "auto-per-task") {
|
|
12742
|
+
return { assignmentMode: "per-task-derived" };
|
|
12743
|
+
}
|
|
12744
|
+
if ((strategy.mode === "existing" || strategy.mode === "custom-new") && strategy.branchName?.trim()) {
|
|
12745
|
+
return { branch: strategy.branchName.trim(), assignmentMode: "shared" };
|
|
12746
|
+
}
|
|
12747
|
+
return { assignmentMode: "shared" };
|
|
12748
|
+
}
|
|
12561
12749
|
function deriveMilestoneAcceptanceCriteriaFromFeatures(features) {
|
|
12562
12750
|
const lines = features.map((feature) => {
|
|
12563
12751
|
const acceptance = feature.acceptanceCriteria?.trim();
|
|
@@ -12606,6 +12794,14 @@ var init_mission_store = __esm({
|
|
|
12606
12794
|
* Convert a database row to a Mission object.
|
|
12607
12795
|
*/
|
|
12608
12796
|
rowToMission(row) {
|
|
12797
|
+
let branchStrategy;
|
|
12798
|
+
if (row.branchStrategy) {
|
|
12799
|
+
try {
|
|
12800
|
+
branchStrategy = JSON.parse(row.branchStrategy);
|
|
12801
|
+
} catch {
|
|
12802
|
+
branchStrategy = void 0;
|
|
12803
|
+
}
|
|
12804
|
+
}
|
|
12609
12805
|
return {
|
|
12610
12806
|
id: row.id,
|
|
12611
12807
|
title: row.title,
|
|
@@ -12613,6 +12809,8 @@ var init_mission_store = __esm({
|
|
|
12613
12809
|
status: row.status,
|
|
12614
12810
|
interviewState: row.interviewState,
|
|
12615
12811
|
baseBranch: row.baseBranch || void 0,
|
|
12812
|
+
branchStrategy,
|
|
12813
|
+
autoMerge: row.autoMerge === null ? void 0 : Boolean(row.autoMerge),
|
|
12616
12814
|
autoAdvance: Boolean(row.autoAdvance),
|
|
12617
12815
|
autopilotEnabled: Boolean(row.autopilotEnabled),
|
|
12618
12816
|
autopilotState: row.autopilotState || "inactive",
|
|
@@ -12649,6 +12847,7 @@ var init_mission_store = __esm({
|
|
|
12649
12847
|
return {
|
|
12650
12848
|
id: row.id,
|
|
12651
12849
|
milestoneId: row.milestoneId,
|
|
12850
|
+
sourceFeatureId: row.sourceFeatureId || void 0,
|
|
12652
12851
|
title: row.title,
|
|
12653
12852
|
assertion: row.assertion,
|
|
12654
12853
|
status: row.status,
|
|
@@ -12776,7 +12975,8 @@ var init_mission_store = __esm({
|
|
|
12776
12975
|
// ── Mission CRUD Operations ────────────────────────────────────────
|
|
12777
12976
|
/**
|
|
12778
12977
|
* Create a new mission.
|
|
12779
|
-
*
|
|
12978
|
+
* Missions are always created stopped (status `planning`, autopilot disabled).
|
|
12979
|
+
* Autopilot is only enabled via an explicit start/update after creation.
|
|
12780
12980
|
*
|
|
12781
12981
|
* @param input - Mission creation input
|
|
12782
12982
|
* @returns The created mission
|
|
@@ -12791,15 +12991,17 @@ var init_mission_store = __esm({
|
|
|
12791
12991
|
status: "planning",
|
|
12792
12992
|
interviewState: "not_started",
|
|
12793
12993
|
baseBranch: input.baseBranch,
|
|
12994
|
+
branchStrategy: input.branchStrategy,
|
|
12995
|
+
autoMerge: input.autoMerge,
|
|
12794
12996
|
autoAdvance: false,
|
|
12795
|
-
autopilotEnabled:
|
|
12997
|
+
autopilotEnabled: false,
|
|
12796
12998
|
autopilotState: "inactive",
|
|
12797
12999
|
createdAt: now,
|
|
12798
13000
|
updatedAt: now
|
|
12799
13001
|
};
|
|
12800
13002
|
this.db.prepare(`
|
|
12801
|
-
INSERT INTO missions (id, title, description, status, interviewState, baseBranch, autoAdvance, autopilotEnabled, autopilotState, createdAt, updatedAt)
|
|
12802
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
13003
|
+
INSERT INTO missions (id, title, description, status, interviewState, baseBranch, branchStrategy, autoMerge, autoAdvance, autopilotEnabled, autopilotState, createdAt, updatedAt)
|
|
13004
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
12803
13005
|
`).run(
|
|
12804
13006
|
mission.id,
|
|
12805
13007
|
mission.title,
|
|
@@ -12807,6 +13009,8 @@ var init_mission_store = __esm({
|
|
|
12807
13009
|
mission.status,
|
|
12808
13010
|
mission.interviewState,
|
|
12809
13011
|
mission.baseBranch ?? null,
|
|
13012
|
+
mission.branchStrategy ? JSON.stringify(mission.branchStrategy) : null,
|
|
13013
|
+
mission.autoMerge === void 0 ? null : mission.autoMerge ? 1 : 0,
|
|
12810
13014
|
mission.autoAdvance ? 1 : 0,
|
|
12811
13015
|
mission.autopilotEnabled ? 1 : 0,
|
|
12812
13016
|
mission.autopilotState ?? "inactive",
|
|
@@ -13273,6 +13477,8 @@ var init_mission_store = __esm({
|
|
|
13273
13477
|
status = ?,
|
|
13274
13478
|
interviewState = ?,
|
|
13275
13479
|
baseBranch = ?,
|
|
13480
|
+
branchStrategy = ?,
|
|
13481
|
+
autoMerge = ?,
|
|
13276
13482
|
autoAdvance = ?,
|
|
13277
13483
|
autopilotEnabled = ?,
|
|
13278
13484
|
autopilotState = ?,
|
|
@@ -13285,6 +13491,8 @@ var init_mission_store = __esm({
|
|
|
13285
13491
|
updated.status,
|
|
13286
13492
|
updated.interviewState,
|
|
13287
13493
|
updated.baseBranch ?? null,
|
|
13494
|
+
updated.branchStrategy ? JSON.stringify(updated.branchStrategy) : null,
|
|
13495
|
+
updated.autoMerge === void 0 ? null : updated.autoMerge ? 1 : 0,
|
|
13288
13496
|
updated.autoAdvance ? 1 : 0,
|
|
13289
13497
|
updated.autopilotEnabled ? 1 : 0,
|
|
13290
13498
|
updated.autopilotState ?? "inactive",
|
|
@@ -13800,7 +14008,8 @@ var init_mission_store = __esm({
|
|
|
13800
14008
|
this.emit("feature:created", feature);
|
|
13801
14009
|
this.recomputeSliceStatus(sliceId);
|
|
13802
14010
|
this.applyDerivedMilestoneAcceptanceCriteria(slice.milestoneId);
|
|
13803
|
-
|
|
14011
|
+
this.ensureFeatureAssertion(feature);
|
|
14012
|
+
return this.getFeature(feature.id) ?? feature;
|
|
13804
14013
|
}
|
|
13805
14014
|
/**
|
|
13806
14015
|
* Get a feature by ID.
|
|
@@ -13885,6 +14094,11 @@ var init_mission_store = __esm({
|
|
|
13885
14094
|
if (taskIdChanged || statusChanged) {
|
|
13886
14095
|
this.recomputeSliceStatus(updated.sliceId);
|
|
13887
14096
|
}
|
|
14097
|
+
const shouldSyncAssertion = updates.title !== void 0 || updates.description !== void 0 || updates.acceptanceCriteria !== void 0;
|
|
14098
|
+
if (shouldSyncAssertion) {
|
|
14099
|
+
this.ensureFeatureAssertion(updated);
|
|
14100
|
+
return this.getFeature(updated.id) ?? updated;
|
|
14101
|
+
}
|
|
13888
14102
|
return updated;
|
|
13889
14103
|
}
|
|
13890
14104
|
/**
|
|
@@ -13899,11 +14113,115 @@ var init_mission_store = __esm({
|
|
|
13899
14113
|
throw new Error(`Feature ${id} not found`);
|
|
13900
14114
|
}
|
|
13901
14115
|
const sliceId = feature.sliceId;
|
|
14116
|
+
const slice = this.getSlice(sliceId);
|
|
14117
|
+
const milestoneId = slice?.milestoneId;
|
|
14118
|
+
if (milestoneId) {
|
|
14119
|
+
const managedAssertion = this.listContractAssertions(milestoneId).find((assertion) => assertion.sourceFeatureId === feature.id);
|
|
14120
|
+
if (managedAssertion) {
|
|
14121
|
+
this.deleteContractAssertion(managedAssertion.id);
|
|
14122
|
+
}
|
|
14123
|
+
}
|
|
13902
14124
|
this.db.prepare("DELETE FROM mission_features WHERE id = ?").run(id);
|
|
13903
14125
|
this.db.bumpLastModified();
|
|
13904
14126
|
this.emit("feature:deleted", id);
|
|
13905
14127
|
this.recomputeSliceStatus(sliceId);
|
|
13906
14128
|
}
|
|
14129
|
+
deriveFeatureAssertion(feature) {
|
|
14130
|
+
const acceptanceCriteria = feature.acceptanceCriteria?.trim();
|
|
14131
|
+
if (acceptanceCriteria) {
|
|
14132
|
+
return { assertionText: acceptanceCriteria, textSource: "acceptanceCriteria" };
|
|
14133
|
+
}
|
|
14134
|
+
const description = feature.description?.trim();
|
|
14135
|
+
if (description) {
|
|
14136
|
+
return { assertionText: description, textSource: "description" };
|
|
14137
|
+
}
|
|
14138
|
+
return {
|
|
14139
|
+
assertionText: `Verify implementation of: ${feature.title}`,
|
|
14140
|
+
textSource: "fallback"
|
|
14141
|
+
};
|
|
14142
|
+
}
|
|
14143
|
+
ensureFeatureAssertion(feature) {
|
|
14144
|
+
const slice = this.getSlice(feature.sliceId);
|
|
14145
|
+
if (!slice) {
|
|
14146
|
+
throw new Error(`Slice ${feature.sliceId} not found`);
|
|
14147
|
+
}
|
|
14148
|
+
const milestoneId = slice.milestoneId;
|
|
14149
|
+
const { assertionText } = this.deriveFeatureAssertion(feature);
|
|
14150
|
+
const existing = this.listContractAssertions(milestoneId).find((assertion) => assertion.sourceFeatureId === feature.id);
|
|
14151
|
+
if (!existing) {
|
|
14152
|
+
const created = this.addContractAssertion(milestoneId, {
|
|
14153
|
+
title: feature.title,
|
|
14154
|
+
assertion: assertionText,
|
|
14155
|
+
status: "pending",
|
|
14156
|
+
sourceFeatureId: feature.id
|
|
14157
|
+
});
|
|
14158
|
+
this.linkFeatureToAssertion(feature.id, created.id);
|
|
14159
|
+
return;
|
|
14160
|
+
}
|
|
14161
|
+
if (existing.title !== feature.title || existing.assertion !== assertionText) {
|
|
14162
|
+
this.updateContractAssertion(existing.id, {
|
|
14163
|
+
title: feature.title,
|
|
14164
|
+
assertion: assertionText
|
|
14165
|
+
});
|
|
14166
|
+
}
|
|
14167
|
+
}
|
|
14168
|
+
/**
|
|
14169
|
+
* Backfill assertion links for legacy features that predate the FN-5695 creation-path fix.
|
|
14170
|
+
* Reuses deriveFeatureAssertion()/ensureFeatureAssertion text-source rules so create/update
|
|
14171
|
+
* and repair flows stay aligned on canonical assertion content.
|
|
14172
|
+
*/
|
|
14173
|
+
backfillFeatureAssertions(options) {
|
|
14174
|
+
const dryRun = options?.dryRun ?? true;
|
|
14175
|
+
const missionFilter = options?.missionId;
|
|
14176
|
+
const features = missionFilter ? this.listMilestones(missionFilter).flatMap((milestone) => this.listSlices(milestone.id)).flatMap((slice) => this.listFeatures(slice.id)) : this.listMissions().flatMap((mission) => this.listMilestones(mission.id)).flatMap((milestone) => this.listSlices(milestone.id)).flatMap((slice) => this.listFeatures(slice.id));
|
|
14177
|
+
const report = {
|
|
14178
|
+
scanned: features.length,
|
|
14179
|
+
alreadyLinked: 0,
|
|
14180
|
+
repaired: [],
|
|
14181
|
+
skippedErrors: []
|
|
14182
|
+
};
|
|
14183
|
+
for (const feature of features) {
|
|
14184
|
+
try {
|
|
14185
|
+
const linkedAssertions = this.listAssertionsForFeature(feature.id);
|
|
14186
|
+
if (linkedAssertions.length > 0) {
|
|
14187
|
+
report.alreadyLinked += 1;
|
|
14188
|
+
continue;
|
|
14189
|
+
}
|
|
14190
|
+
const slice = this.getSlice(feature.sliceId);
|
|
14191
|
+
if (!slice) {
|
|
14192
|
+
throw new Error(`Slice ${feature.sliceId} not found`);
|
|
14193
|
+
}
|
|
14194
|
+
const milestoneId = slice.milestoneId;
|
|
14195
|
+
const { assertionText, textSource } = this.deriveFeatureAssertion(feature);
|
|
14196
|
+
if (dryRun) {
|
|
14197
|
+
report.repaired.push({
|
|
14198
|
+
featureId: feature.id,
|
|
14199
|
+
milestoneId,
|
|
14200
|
+
assertionId: "(dry-run)",
|
|
14201
|
+
textSource
|
|
14202
|
+
});
|
|
14203
|
+
continue;
|
|
14204
|
+
}
|
|
14205
|
+
const created = this.addContractAssertion(milestoneId, {
|
|
14206
|
+
title: feature.title,
|
|
14207
|
+
assertion: assertionText,
|
|
14208
|
+
status: "pending",
|
|
14209
|
+
sourceFeatureId: feature.id
|
|
14210
|
+
});
|
|
14211
|
+
this.linkFeatureToAssertion(feature.id, created.id);
|
|
14212
|
+
report.repaired.push({
|
|
14213
|
+
featureId: feature.id,
|
|
14214
|
+
milestoneId,
|
|
14215
|
+
assertionId: created.id,
|
|
14216
|
+
textSource
|
|
14217
|
+
});
|
|
14218
|
+
} catch (error) {
|
|
14219
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
14220
|
+
report.skippedErrors.push({ featureId: feature.id, message });
|
|
14221
|
+
}
|
|
14222
|
+
}
|
|
14223
|
+
return report;
|
|
14224
|
+
}
|
|
13907
14225
|
/**
|
|
13908
14226
|
* Resolve the mission hierarchy for a slice.
|
|
13909
14227
|
*
|
|
@@ -14496,6 +14814,7 @@ var init_mission_store = __esm({
|
|
|
14496
14814
|
const assertion = {
|
|
14497
14815
|
id,
|
|
14498
14816
|
milestoneId,
|
|
14817
|
+
sourceFeatureId: input.sourceFeatureId,
|
|
14499
14818
|
title: input.title,
|
|
14500
14819
|
assertion: input.assertion,
|
|
14501
14820
|
status: input.status || "pending",
|
|
@@ -14504,8 +14823,8 @@ var init_mission_store = __esm({
|
|
|
14504
14823
|
updatedAt: now
|
|
14505
14824
|
};
|
|
14506
14825
|
this.db.prepare(`
|
|
14507
|
-
INSERT INTO mission_contract_assertions (id, milestoneId, title, assertion, status, orderIndex, createdAt, updatedAt)
|
|
14508
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
|
14826
|
+
INSERT INTO mission_contract_assertions (id, milestoneId, title, assertion, status, orderIndex, sourceFeatureId, createdAt, updatedAt)
|
|
14827
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
14509
14828
|
`).run(
|
|
14510
14829
|
assertion.id,
|
|
14511
14830
|
assertion.milestoneId,
|
|
@@ -14513,6 +14832,7 @@ var init_mission_store = __esm({
|
|
|
14513
14832
|
assertion.assertion,
|
|
14514
14833
|
assertion.status,
|
|
14515
14834
|
assertion.orderIndex,
|
|
14835
|
+
assertion.sourceFeatureId ?? null,
|
|
14516
14836
|
assertion.createdAt,
|
|
14517
14837
|
assertion.updatedAt
|
|
14518
14838
|
);
|
|
@@ -14954,7 +15274,10 @@ ${feature.acceptanceCriteria}`);
|
|
|
14954
15274
|
const milestone = slice ? this.getMilestone(slice.milestoneId) : void 0;
|
|
14955
15275
|
const missionId = milestone?.missionId;
|
|
14956
15276
|
const mission = missionId ? this.getMission(missionId) : void 0;
|
|
15277
|
+
const strategyDefaults = missionBranchStrategyDefaults(mission?.branchStrategy);
|
|
14957
15278
|
const resolvedBaseBranch = branchOptions?.baseBranch ?? mission?.baseBranch;
|
|
15279
|
+
const resolvedBranch = branchOptions?.branch ?? strategyDefaults.branch;
|
|
15280
|
+
const resolvedAssignmentMode = branchOptions?.assignmentMode ?? strategyDefaults.assignmentMode;
|
|
14958
15281
|
const lockScope = missionId ? `mission:${missionId}` : `mission-store:${this.taskStore.getRootDir()}`;
|
|
14959
15282
|
const guard = await runDeterministicDuplicateGuard(this.taskStore, {
|
|
14960
15283
|
title: taskTitle || feature.title,
|
|
@@ -14968,13 +15291,13 @@ ${feature.acceptanceCriteria}`);
|
|
|
14968
15291
|
const createdTask = await this.taskStore.createTask({
|
|
14969
15292
|
title: taskTitle || feature.title,
|
|
14970
15293
|
description,
|
|
14971
|
-
branch:
|
|
15294
|
+
branch: resolvedBranch,
|
|
14972
15295
|
baseBranch: resolvedBaseBranch,
|
|
14973
15296
|
...missionId ? {
|
|
14974
15297
|
branchContext: {
|
|
14975
15298
|
groupId: `mission:${missionId}`,
|
|
14976
15299
|
source: "mission",
|
|
14977
|
-
assignmentMode:
|
|
15300
|
+
assignmentMode: resolvedAssignmentMode,
|
|
14978
15301
|
inheritedBaseBranch: resolvedBaseBranch
|
|
14979
15302
|
}
|
|
14980
15303
|
} : {}
|
|
@@ -15019,14 +15342,18 @@ ${feature.acceptanceCriteria}`);
|
|
|
15019
15342
|
const definedFeatures = features.filter((f2) => f2.status === "defined");
|
|
15020
15343
|
const milestone = this.getMilestone(slice.milestoneId);
|
|
15021
15344
|
const mission = milestone ? this.getMission(milestone.missionId) : void 0;
|
|
15345
|
+
const strategyDefaults = missionBranchStrategyDefaults(mission?.branchStrategy);
|
|
15022
15346
|
const resolvedBaseBranch = branchOptions?.baseBranch ?? mission?.baseBranch;
|
|
15347
|
+
const resolvedAssignmentMode = branchOptions?.assignmentMode ?? strategyDefaults.assignmentMode;
|
|
15348
|
+
const resolvedBranch = branchOptions?.branch ?? strategyDefaults.branch;
|
|
15023
15349
|
const triaged = [];
|
|
15024
15350
|
for (const feature of definedFeatures) {
|
|
15025
|
-
const
|
|
15351
|
+
const strategyBranch = resolvedAssignmentMode === "per-task-derived" ? resolvedBranch ? `${resolvedBranch}/${feature.id.toLowerCase()}` : void 0 : resolvedBranch;
|
|
15026
15352
|
const updated = await this.triageFeature(feature.id, void 0, void 0, {
|
|
15027
|
-
|
|
15028
|
-
|
|
15029
|
-
|
|
15353
|
+
branch: strategyBranch,
|
|
15354
|
+
baseBranch: resolvedBaseBranch,
|
|
15355
|
+
assignmentMode: resolvedAssignmentMode,
|
|
15356
|
+
...branchOptions
|
|
15030
15357
|
});
|
|
15031
15358
|
triaged.push(updated);
|
|
15032
15359
|
}
|
|
@@ -15178,17 +15505,20 @@ ${feature.acceptanceCriteria}`);
|
|
|
15178
15505
|
validateSnapshotEnvelope(snapshot);
|
|
15179
15506
|
let applied = 0;
|
|
15180
15507
|
for (const mission of snapshot.payload.missions) {
|
|
15181
|
-
this.db.prepare(`INSERT INTO missions (id, title, description, status, interviewState, autoAdvance, autopilotEnabled, autopilotState, lastAutopilotActivityAt, createdAt, updatedAt)
|
|
15182
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
15508
|
+
this.db.prepare(`INSERT INTO missions (id, title, description, status, interviewState, baseBranch, branchStrategy, autoMerge, autoAdvance, autopilotEnabled, autopilotState, lastAutopilotActivityAt, createdAt, updatedAt)
|
|
15509
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
15183
15510
|
ON CONFLICT(id) DO UPDATE SET
|
|
15184
15511
|
title=excluded.title, description=excluded.description, status=excluded.status, interviewState=excluded.interviewState,
|
|
15185
|
-
autoAdvance=excluded.autoAdvance, autopilotEnabled=excluded.autopilotEnabled, autopilotState=excluded.autopilotState,
|
|
15512
|
+
baseBranch=excluded.baseBranch, branchStrategy=excluded.branchStrategy, autoMerge=excluded.autoMerge, autoAdvance=excluded.autoAdvance, autopilotEnabled=excluded.autopilotEnabled, autopilotState=excluded.autopilotState,
|
|
15186
15513
|
lastAutopilotActivityAt=excluded.lastAutopilotActivityAt, updatedAt=excluded.updatedAt`).run(
|
|
15187
15514
|
mission.id,
|
|
15188
15515
|
mission.title,
|
|
15189
15516
|
mission.description ?? null,
|
|
15190
15517
|
mission.status,
|
|
15191
15518
|
mission.interviewState,
|
|
15519
|
+
mission.baseBranch ?? null,
|
|
15520
|
+
mission.branchStrategy ? JSON.stringify(mission.branchStrategy) : null,
|
|
15521
|
+
mission.autoMerge === void 0 ? null : mission.autoMerge ? 1 : 0,
|
|
15192
15522
|
mission.autoAdvance ? 1 : 0,
|
|
15193
15523
|
mission.autopilotEnabled ? 1 : 0,
|
|
15194
15524
|
mission.autopilotState,
|
|
@@ -15274,9 +15604,9 @@ ${feature.acceptanceCriteria}`);
|
|
|
15274
15604
|
}
|
|
15275
15605
|
for (const assertion of snapshot.payload.assertions) {
|
|
15276
15606
|
if (!assertion.id || !assertion.milestoneId) continue;
|
|
15277
|
-
this.db.prepare(`INSERT INTO mission_contract_assertions (id, milestoneId, title, assertion, status, orderIndex, createdAt, updatedAt)
|
|
15278
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
|
15279
|
-
ON CONFLICT(id) DO UPDATE SET title=excluded.title, assertion=excluded.assertion, status=excluded.status, orderIndex=excluded.orderIndex, updatedAt=excluded.updatedAt`).run(assertion.id, assertion.milestoneId, assertion.title, assertion.assertion, assertion.status, assertion.orderIndex, assertion.createdAt, assertion.updatedAt);
|
|
15607
|
+
this.db.prepare(`INSERT INTO mission_contract_assertions (id, milestoneId, title, assertion, status, orderIndex, sourceFeatureId, createdAt, updatedAt)
|
|
15608
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
15609
|
+
ON CONFLICT(id) DO UPDATE SET title=excluded.title, assertion=excluded.assertion, status=excluded.status, orderIndex=excluded.orderIndex, sourceFeatureId=excluded.sourceFeatureId, updatedAt=excluded.updatedAt`).run(assertion.id, assertion.milestoneId, assertion.title, assertion.assertion, assertion.status, assertion.orderIndex, assertion.sourceFeatureId ?? null, assertion.createdAt, assertion.updatedAt);
|
|
15280
15610
|
}
|
|
15281
15611
|
for (const link of snapshot.payload.featureAssertionLinks) {
|
|
15282
15612
|
if (!link.featureId || !link.assertionId) continue;
|
|
@@ -27237,6 +27567,9 @@ var init_secrets_sync_passphrase = __esm({
|
|
|
27237
27567
|
function isFusionSiblingBranch(branch) {
|
|
27238
27568
|
return FUSION_SIBLING_BRANCH_RE.test(branch);
|
|
27239
27569
|
}
|
|
27570
|
+
function resolveEffectiveAutoMerge(task, settings) {
|
|
27571
|
+
return task.autoMerge ?? settings.autoMerge;
|
|
27572
|
+
}
|
|
27240
27573
|
function resolveTaskMergeTarget(task, options = {}) {
|
|
27241
27574
|
let rejected;
|
|
27242
27575
|
const configuredBase = task.baseBranch?.trim();
|
|
@@ -40159,6 +40492,7 @@ var init_store = __esm({
|
|
|
40159
40492
|
init_db();
|
|
40160
40493
|
init_archive_db();
|
|
40161
40494
|
init_db_migrate();
|
|
40495
|
+
init_goal_citation_extractor();
|
|
40162
40496
|
init_mission_store();
|
|
40163
40497
|
init_plugin_store();
|
|
40164
40498
|
init_insight_store();
|
|
@@ -40688,6 +41022,7 @@ var init_store = __esm({
|
|
|
40688
41022
|
baseBranch: row.baseBranch || void 0,
|
|
40689
41023
|
executionStartBranch: row.executionStartBranch || void 0,
|
|
40690
41024
|
branch: row.branch || void 0,
|
|
41025
|
+
autoMerge: row.autoMerge === null ? void 0 : row.autoMerge === 1,
|
|
40691
41026
|
baseCommitSha: row.baseCommitSha || void 0,
|
|
40692
41027
|
scopeOverride: row.scopeOverride ? true : void 0,
|
|
40693
41028
|
scopeOverrideReason: row.scopeOverrideReason || void 0,
|
|
@@ -40702,6 +41037,9 @@ var init_store = __esm({
|
|
|
40702
41037
|
mergeRetries: row.mergeRetries ?? void 0,
|
|
40703
41038
|
workflowStepRetries: row.workflowStepRetries ?? void 0,
|
|
40704
41039
|
stuckKillCount: row.stuckKillCount ?? void 0,
|
|
41040
|
+
resumeLimboCount: row.resumeLimboCount ?? void 0,
|
|
41041
|
+
resumeLimboTipSha: row.resumeLimboTipSha || void 0,
|
|
41042
|
+
resumeLimboStepSignature: row.resumeLimboStepSignature || void 0,
|
|
40705
41043
|
postReviewFixCount: row.postReviewFixCount ?? void 0,
|
|
40706
41044
|
recoveryRetryCount: row.recoveryRetryCount ?? void 0,
|
|
40707
41045
|
taskDoneRetryCount: row.taskDoneRetryCount ?? void 0,
|
|
@@ -40710,6 +41048,7 @@ var init_store = __esm({
|
|
|
40710
41048
|
verificationFailureCount: row.verificationFailureCount ?? void 0,
|
|
40711
41049
|
mergeConflictBounceCount: row.mergeConflictBounceCount ?? void 0,
|
|
40712
41050
|
mergeAuditBounceCount: row.mergeAuditBounceCount ?? void 0,
|
|
41051
|
+
mergeTransientRetryCount: row.mergeTransientRetryCount ?? void 0,
|
|
40713
41052
|
branchConflictRecoveryCount: row.branchConflictRecoveryCount ?? void 0,
|
|
40714
41053
|
reviewerContextRetryCount: row.reviewerContextRetryCount ?? void 0,
|
|
40715
41054
|
reviewerFallbackRetryCount: row.reviewerFallbackRetryCount ?? void 0,
|
|
@@ -40833,6 +41172,28 @@ var init_store = __esm({
|
|
|
40833
41172
|
allowResurrection: row.allowResurrection ? true : void 0
|
|
40834
41173
|
};
|
|
40835
41174
|
}
|
|
41175
|
+
rowToBranchGroup(row) {
|
|
41176
|
+
return {
|
|
41177
|
+
id: row.id,
|
|
41178
|
+
sourceType: row.sourceType,
|
|
41179
|
+
sourceId: row.sourceId,
|
|
41180
|
+
branchName: row.branchName,
|
|
41181
|
+
worktreePath: row.worktreePath ?? void 0,
|
|
41182
|
+
autoMerge: Boolean(row.autoMerge),
|
|
41183
|
+
prState: row.prState,
|
|
41184
|
+
prUrl: row.prUrl ?? void 0,
|
|
41185
|
+
prNumber: row.prNumber ?? void 0,
|
|
41186
|
+
status: row.status,
|
|
41187
|
+
createdAt: row.createdAt,
|
|
41188
|
+
updatedAt: row.updatedAt,
|
|
41189
|
+
closedAt: row.closedAt ?? void 0
|
|
41190
|
+
};
|
|
41191
|
+
}
|
|
41192
|
+
generateBranchGroupId() {
|
|
41193
|
+
const timestamp = Date.now().toString(36).toUpperCase();
|
|
41194
|
+
const random = Math.random().toString(36).slice(2, 8).toUpperCase();
|
|
41195
|
+
return `BG-${timestamp}-${random}`;
|
|
41196
|
+
}
|
|
40836
41197
|
archiveEntryToTask(entry, slim = false) {
|
|
40837
41198
|
return {
|
|
40838
41199
|
id: entry.id,
|
|
@@ -40872,6 +41233,8 @@ var init_store = __esm({
|
|
|
40872
41233
|
planningModelId: entry.planningModelId,
|
|
40873
41234
|
breakIntoSubtasks: entry.breakIntoSubtasks,
|
|
40874
41235
|
noCommitsExpected: entry.noCommitsExpected,
|
|
41236
|
+
branchContext: entry.branchContext,
|
|
41237
|
+
autoMerge: entry.autoMerge,
|
|
40875
41238
|
modifiedFiles: slim ? void 0 : entry.modifiedFiles,
|
|
40876
41239
|
missionId: entry.missionId,
|
|
40877
41240
|
sliceId: entry.sliceId,
|
|
@@ -40985,6 +41348,8 @@ ${recentText}` : void 0
|
|
|
40985
41348
|
noCommitsExpected: task.noCommitsExpected,
|
|
40986
41349
|
baseBranch: task.baseBranch,
|
|
40987
41350
|
branch: task.branch,
|
|
41351
|
+
branchContext: task.branchContext,
|
|
41352
|
+
autoMerge: task.autoMerge,
|
|
40988
41353
|
baseCommitSha: task.baseCommitSha,
|
|
40989
41354
|
mergeRetries: task.mergeRetries,
|
|
40990
41355
|
error: task.error,
|
|
@@ -41025,6 +41390,99 @@ ${recentText}` : void 0
|
|
|
41025
41390
|
createdAt: row.createdAt
|
|
41026
41391
|
};
|
|
41027
41392
|
}
|
|
41393
|
+
rowToGoalCitation(row) {
|
|
41394
|
+
return {
|
|
41395
|
+
id: row.id,
|
|
41396
|
+
goalId: row.goalId,
|
|
41397
|
+
agentId: row.agentId,
|
|
41398
|
+
...row.taskId ? { taskId: row.taskId } : {},
|
|
41399
|
+
surface: row.surface,
|
|
41400
|
+
sourceRef: row.sourceRef,
|
|
41401
|
+
snippet: row.snippet,
|
|
41402
|
+
timestamp: row.timestamp
|
|
41403
|
+
};
|
|
41404
|
+
}
|
|
41405
|
+
recordGoalCitations(inputs) {
|
|
41406
|
+
if (inputs.length === 0) {
|
|
41407
|
+
return [];
|
|
41408
|
+
}
|
|
41409
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
41410
|
+
const stmt = this.db.prepare(`
|
|
41411
|
+
INSERT OR IGNORE INTO goal_citations (goalId, agentId, taskId, surface, sourceRef, snippet, timestamp)
|
|
41412
|
+
VALUES (?, ?, ?, ?, ?, ?, ?)
|
|
41413
|
+
RETURNING *
|
|
41414
|
+
`);
|
|
41415
|
+
const inserted = [];
|
|
41416
|
+
this.db.transaction(() => {
|
|
41417
|
+
for (const input of inputs) {
|
|
41418
|
+
const row = stmt.get(
|
|
41419
|
+
input.goalId,
|
|
41420
|
+
input.agentId,
|
|
41421
|
+
input.taskId ?? null,
|
|
41422
|
+
input.surface,
|
|
41423
|
+
input.sourceRef,
|
|
41424
|
+
input.snippet,
|
|
41425
|
+
input.timestamp ?? now
|
|
41426
|
+
);
|
|
41427
|
+
if (row) {
|
|
41428
|
+
inserted.push(this.rowToGoalCitation(row));
|
|
41429
|
+
}
|
|
41430
|
+
}
|
|
41431
|
+
if (inserted.length > 0) {
|
|
41432
|
+
this.db.bumpLastModified();
|
|
41433
|
+
}
|
|
41434
|
+
});
|
|
41435
|
+
return inserted;
|
|
41436
|
+
}
|
|
41437
|
+
listGoalCitations(filter = {}) {
|
|
41438
|
+
const clauses = [];
|
|
41439
|
+
const params = [];
|
|
41440
|
+
if (filter.goalId) {
|
|
41441
|
+
clauses.push("goalId = ?");
|
|
41442
|
+
params.push(filter.goalId);
|
|
41443
|
+
}
|
|
41444
|
+
if (filter.agentId) {
|
|
41445
|
+
clauses.push("agentId = ?");
|
|
41446
|
+
params.push(filter.agentId);
|
|
41447
|
+
}
|
|
41448
|
+
if (filter.taskId) {
|
|
41449
|
+
clauses.push("taskId = ?");
|
|
41450
|
+
params.push(filter.taskId);
|
|
41451
|
+
}
|
|
41452
|
+
if (filter.surface) {
|
|
41453
|
+
clauses.push("surface = ?");
|
|
41454
|
+
params.push(filter.surface);
|
|
41455
|
+
}
|
|
41456
|
+
if (filter.startTime) {
|
|
41457
|
+
clauses.push("timestamp >= ?");
|
|
41458
|
+
params.push(filter.startTime);
|
|
41459
|
+
}
|
|
41460
|
+
if (filter.endTime) {
|
|
41461
|
+
clauses.push("timestamp <= ?");
|
|
41462
|
+
params.push(filter.endTime);
|
|
41463
|
+
}
|
|
41464
|
+
const where = clauses.length > 0 ? `WHERE ${clauses.join(" AND ")}` : "";
|
|
41465
|
+
const limit = Math.max(1, Math.min(filter.limit ?? 200, 1e3));
|
|
41466
|
+
const rows = this.db.prepare(
|
|
41467
|
+
`SELECT * FROM goal_citations ${where} ORDER BY timestamp DESC, id DESC LIMIT ?`
|
|
41468
|
+
).all(...params, limit);
|
|
41469
|
+
return rows.map((row) => this.rowToGoalCitation(row));
|
|
41470
|
+
}
|
|
41471
|
+
scanAndRecordCitations(text, surface, sourceRef, agentId, taskId, timestamp) {
|
|
41472
|
+
const matches = extractGoalCitations(text);
|
|
41473
|
+
if (matches.length === 0) {
|
|
41474
|
+
return [];
|
|
41475
|
+
}
|
|
41476
|
+
return matches.map((match) => ({
|
|
41477
|
+
goalId: match.goalId,
|
|
41478
|
+
agentId,
|
|
41479
|
+
...taskId ? { taskId } : {},
|
|
41480
|
+
surface,
|
|
41481
|
+
sourceRef,
|
|
41482
|
+
snippet: buildSnippet(text, match.index),
|
|
41483
|
+
...timestamp ? { timestamp } : {}
|
|
41484
|
+
}));
|
|
41485
|
+
}
|
|
41028
41486
|
getTaskSelectClause(slim, tableAlias) {
|
|
41029
41487
|
if (!slim) {
|
|
41030
41488
|
return tableAlias ? `${tableAlias}.*` : "*";
|
|
@@ -41049,6 +41507,7 @@ ${recentText}` : void 0
|
|
|
41049
41507
|
"userPaused",
|
|
41050
41508
|
"baseBranch",
|
|
41051
41509
|
"branch",
|
|
41510
|
+
"autoMerge",
|
|
41052
41511
|
"executionStartBranch",
|
|
41053
41512
|
"baseCommitSha",
|
|
41054
41513
|
"modelPresetId",
|
|
@@ -41061,6 +41520,9 @@ ${recentText}` : void 0
|
|
|
41061
41520
|
"mergeRetries",
|
|
41062
41521
|
"workflowStepRetries",
|
|
41063
41522
|
"stuckKillCount",
|
|
41523
|
+
"resumeLimboCount",
|
|
41524
|
+
"resumeLimboTipSha",
|
|
41525
|
+
"resumeLimboStepSignature",
|
|
41064
41526
|
"postReviewFixCount",
|
|
41065
41527
|
"recoveryRetryCount",
|
|
41066
41528
|
"taskDoneRetryCount",
|
|
@@ -41069,6 +41531,7 @@ ${recentText}` : void 0
|
|
|
41069
41531
|
"verificationFailureCount",
|
|
41070
41532
|
"mergeConflictBounceCount",
|
|
41071
41533
|
"mergeAuditBounceCount",
|
|
41534
|
+
"mergeTransientRetryCount",
|
|
41072
41535
|
"branchConflictRecoveryCount",
|
|
41073
41536
|
"reviewerContextRetryCount",
|
|
41074
41537
|
"reviewerFallbackRetryCount",
|
|
@@ -41193,6 +41656,7 @@ ${outcome}`;
|
|
|
41193
41656
|
"userPaused",
|
|
41194
41657
|
"baseBranch",
|
|
41195
41658
|
"branch",
|
|
41659
|
+
"autoMerge",
|
|
41196
41660
|
"executionStartBranch",
|
|
41197
41661
|
"baseCommitSha",
|
|
41198
41662
|
"modelPresetId",
|
|
@@ -41205,6 +41669,9 @@ ${outcome}`;
|
|
|
41205
41669
|
"mergeRetries",
|
|
41206
41670
|
"workflowStepRetries",
|
|
41207
41671
|
"stuckKillCount",
|
|
41672
|
+
"resumeLimboCount",
|
|
41673
|
+
"resumeLimboTipSha",
|
|
41674
|
+
"resumeLimboStepSignature",
|
|
41208
41675
|
"postReviewFixCount",
|
|
41209
41676
|
"recoveryRetryCount",
|
|
41210
41677
|
"taskDoneRetryCount",
|
|
@@ -41213,6 +41680,7 @@ ${outcome}`;
|
|
|
41213
41680
|
"verificationFailureCount",
|
|
41214
41681
|
"mergeConflictBounceCount",
|
|
41215
41682
|
"mergeAuditBounceCount",
|
|
41683
|
+
"mergeTransientRetryCount",
|
|
41216
41684
|
"branchConflictRecoveryCount",
|
|
41217
41685
|
"reviewerContextRetryCount",
|
|
41218
41686
|
"reviewerFallbackRetryCount",
|
|
@@ -41326,6 +41794,7 @@ ${outcome}`;
|
|
|
41326
41794
|
task.userPaused ? 1 : 0,
|
|
41327
41795
|
task.baseBranch ?? null,
|
|
41328
41796
|
task.branch ?? null,
|
|
41797
|
+
task.autoMerge === void 0 ? null : task.autoMerge ? 1 : 0,
|
|
41329
41798
|
task.executionStartBranch ?? null,
|
|
41330
41799
|
task.baseCommitSha ?? null,
|
|
41331
41800
|
task.modelPresetId ?? null,
|
|
@@ -41338,6 +41807,9 @@ ${outcome}`;
|
|
|
41338
41807
|
task.mergeRetries ?? null,
|
|
41339
41808
|
task.workflowStepRetries ?? null,
|
|
41340
41809
|
task.stuckKillCount ?? 0,
|
|
41810
|
+
task.resumeLimboCount ?? 0,
|
|
41811
|
+
task.resumeLimboTipSha ?? null,
|
|
41812
|
+
task.resumeLimboStepSignature ?? null,
|
|
41341
41813
|
task.postReviewFixCount ?? 0,
|
|
41342
41814
|
task.recoveryRetryCount ?? null,
|
|
41343
41815
|
task.taskDoneRetryCount ?? 0,
|
|
@@ -41346,6 +41818,7 @@ ${outcome}`;
|
|
|
41346
41818
|
task.verificationFailureCount ?? 0,
|
|
41347
41819
|
task.mergeConflictBounceCount ?? 0,
|
|
41348
41820
|
task.mergeAuditBounceCount ?? 0,
|
|
41821
|
+
task.mergeTransientRetryCount ?? 0,
|
|
41349
41822
|
task.branchConflictRecoveryCount ?? 0,
|
|
41350
41823
|
task.reviewerContextRetryCount ?? 0,
|
|
41351
41824
|
task.reviewerFallbackRetryCount ?? 0,
|
|
@@ -41392,6 +41865,7 @@ ${outcome}`;
|
|
|
41392
41865
|
toJsonNullable(task.mergeDetails),
|
|
41393
41866
|
task.breakIntoSubtasks ? 1 : 0,
|
|
41394
41867
|
task.noCommitsExpected ? 1 : 0,
|
|
41868
|
+
task.autoMerge === void 0 ? null : task.autoMerge ? 1 : 0,
|
|
41395
41869
|
toJson(task.enabledWorkflowSteps || []),
|
|
41396
41870
|
toJson(task.modifiedFiles || []),
|
|
41397
41871
|
task.missionId ?? null,
|
|
@@ -41432,16 +41906,16 @@ ${outcome}`;
|
|
|
41432
41906
|
this.db.prepare(`
|
|
41433
41907
|
INSERT INTO tasks (
|
|
41434
41908
|
id, lineageId, title, description, priority, "column", status, size, reviewLevel, currentStep,
|
|
41435
|
-
worktree, blockedBy, overlapBlockedBy, paused, userPaused, baseBranch, branch, executionStartBranch, baseCommitSha, modelPresetId, modelProvider,
|
|
41909
|
+
worktree, blockedBy, overlapBlockedBy, paused, userPaused, baseBranch, branch, autoMerge, executionStartBranch, baseCommitSha, modelPresetId, modelProvider,
|
|
41436
41910
|
modelId, validatorModelProvider, validatorModelId, planningModelProvider, planningModelId, mergeRetries,
|
|
41437
|
-
workflowStepRetries, stuckKillCount, postReviewFixCount, recoveryRetryCount, taskDoneRetryCount, worktreeSessionRetryCount, completionHandoffLimboRecoveryCount, verificationFailureCount, mergeConflictBounceCount, mergeAuditBounceCount, branchConflictRecoveryCount, reviewerContextRetryCount, reviewerFallbackRetryCount, nextRecoveryAt, error,
|
|
41911
|
+
workflowStepRetries, stuckKillCount, resumeLimboCount, resumeLimboTipSha, resumeLimboStepSignature, postReviewFixCount, recoveryRetryCount, taskDoneRetryCount, worktreeSessionRetryCount, completionHandoffLimboRecoveryCount, verificationFailureCount, mergeConflictBounceCount, mergeAuditBounceCount, mergeTransientRetryCount, branchConflictRecoveryCount, reviewerContextRetryCount, reviewerFallbackRetryCount, nextRecoveryAt, error,
|
|
41438
41912
|
summary, thinkingLevel, executionMode, tokenUsageInputTokens, tokenUsageOutputTokens, tokenUsageCachedTokens,
|
|
41439
41913
|
tokenUsageCacheWriteTokens, tokenUsageTotalTokens, tokenUsageFirstUsedAt, tokenUsageLastUsedAt, tokenBudgetSoftAlertedAt, tokenBudgetHardAlertedAt, tokenBudgetOverride, createdAt, updatedAt, columnMovedAt,
|
|
41440
41914
|
firstExecutionAt, cumulativeActiveMs, executionStartedAt, executionCompletedAt,
|
|
41441
41915
|
dependencies, steps, log, attachments, steeringComments,
|
|
41442
41916
|
comments, review, reviewState, workflowStepResults, prInfo, prInfos, issueInfo, githubTracking,
|
|
41443
41917
|
sourceIssueProvider, sourceIssueRepository, sourceIssueExternalIssueId, sourceIssueNumber, sourceIssueUrl,
|
|
41444
|
-
mergeDetails, breakIntoSubtasks, noCommitsExpected, enabledWorkflowSteps, modifiedFiles, missionId, sliceId, scopeOverride, scopeOverrideReason, scopeAutoWiden, assignedAgentId, pausedByAgentId, assigneeUserId, nodeId, effectiveNodeId, effectiveNodeSource, sourceType, sourceAgentId, sourceRunId, sourceSessionId, sourceMessageId, sourceParentTaskId, sourceMetadata, checkedOutBy, checkedOutAt, checkoutNodeId, checkoutRunId, checkoutLeaseRenewedAt, checkoutLeaseEpoch, deletedAt, allowResurrection
|
|
41918
|
+
mergeDetails, breakIntoSubtasks, noCommitsExpected, autoMerge, enabledWorkflowSteps, modifiedFiles, missionId, sliceId, scopeOverride, scopeOverrideReason, scopeAutoWiden, assignedAgentId, pausedByAgentId, assigneeUserId, nodeId, effectiveNodeId, effectiveNodeSource, sourceType, sourceAgentId, sourceRunId, sourceSessionId, sourceMessageId, sourceParentTaskId, sourceMetadata, checkedOutBy, checkedOutAt, checkoutNodeId, checkoutRunId, checkoutLeaseRenewedAt, checkoutLeaseEpoch, deletedAt, allowResurrection
|
|
41445
41919
|
) VALUES (${placeholders})
|
|
41446
41920
|
`).run(...values);
|
|
41447
41921
|
this.db.bumpLastModified();
|
|
@@ -41458,16 +41932,16 @@ ${outcome}`;
|
|
|
41458
41932
|
this.db.prepare(`
|
|
41459
41933
|
INSERT INTO tasks (
|
|
41460
41934
|
id, lineageId, title, description, priority, "column", status, size, reviewLevel, currentStep,
|
|
41461
|
-
worktree, blockedBy, overlapBlockedBy, paused, userPaused, baseBranch, branch, executionStartBranch, baseCommitSha, modelPresetId, modelProvider,
|
|
41935
|
+
worktree, blockedBy, overlapBlockedBy, paused, userPaused, baseBranch, branch, autoMerge, executionStartBranch, baseCommitSha, modelPresetId, modelProvider,
|
|
41462
41936
|
modelId, validatorModelProvider, validatorModelId, planningModelProvider, planningModelId, mergeRetries,
|
|
41463
|
-
workflowStepRetries, stuckKillCount, postReviewFixCount, recoveryRetryCount, taskDoneRetryCount, worktreeSessionRetryCount, completionHandoffLimboRecoveryCount, verificationFailureCount, mergeConflictBounceCount, mergeAuditBounceCount, branchConflictRecoveryCount, reviewerContextRetryCount, reviewerFallbackRetryCount, nextRecoveryAt, error,
|
|
41937
|
+
workflowStepRetries, stuckKillCount, resumeLimboCount, resumeLimboTipSha, resumeLimboStepSignature, postReviewFixCount, recoveryRetryCount, taskDoneRetryCount, worktreeSessionRetryCount, completionHandoffLimboRecoveryCount, verificationFailureCount, mergeConflictBounceCount, mergeAuditBounceCount, mergeTransientRetryCount, branchConflictRecoveryCount, reviewerContextRetryCount, reviewerFallbackRetryCount, nextRecoveryAt, error,
|
|
41464
41938
|
summary, thinkingLevel, executionMode, tokenUsageInputTokens, tokenUsageOutputTokens, tokenUsageCachedTokens,
|
|
41465
41939
|
tokenUsageCacheWriteTokens, tokenUsageTotalTokens, tokenUsageFirstUsedAt, tokenUsageLastUsedAt, tokenBudgetSoftAlertedAt, tokenBudgetHardAlertedAt, tokenBudgetOverride, createdAt, updatedAt, columnMovedAt,
|
|
41466
41940
|
firstExecutionAt, cumulativeActiveMs, executionStartedAt, executionCompletedAt,
|
|
41467
41941
|
dependencies, steps, log, attachments, steeringComments,
|
|
41468
41942
|
comments, review, reviewState, workflowStepResults, prInfo, prInfos, issueInfo, githubTracking,
|
|
41469
41943
|
sourceIssueProvider, sourceIssueRepository, sourceIssueExternalIssueId, sourceIssueNumber, sourceIssueUrl,
|
|
41470
|
-
mergeDetails, breakIntoSubtasks, noCommitsExpected, enabledWorkflowSteps, modifiedFiles, missionId, sliceId, scopeOverride, scopeOverrideReason, scopeAutoWiden, assignedAgentId, pausedByAgentId, assigneeUserId, nodeId, effectiveNodeId, effectiveNodeSource, sourceType, sourceAgentId, sourceRunId, sourceSessionId, sourceMessageId, sourceParentTaskId, sourceMetadata, checkedOutBy, checkedOutAt, checkoutNodeId, checkoutRunId, checkoutLeaseRenewedAt, checkoutLeaseEpoch, deletedAt, allowResurrection
|
|
41944
|
+
mergeDetails, breakIntoSubtasks, noCommitsExpected, autoMerge, enabledWorkflowSteps, modifiedFiles, missionId, sliceId, scopeOverride, scopeOverrideReason, scopeAutoWiden, assignedAgentId, pausedByAgentId, assigneeUserId, nodeId, effectiveNodeId, effectiveNodeSource, sourceType, sourceAgentId, sourceRunId, sourceSessionId, sourceMessageId, sourceParentTaskId, sourceMetadata, checkedOutBy, checkedOutAt, checkoutNodeId, checkoutRunId, checkoutLeaseRenewedAt, checkoutLeaseEpoch, deletedAt, allowResurrection
|
|
41471
41945
|
) VALUES (${placeholders})
|
|
41472
41946
|
ON CONFLICT(id) DO UPDATE SET
|
|
41473
41947
|
lineageId = excluded.lineageId,
|
|
@@ -41486,6 +41960,7 @@ ${outcome}`;
|
|
|
41486
41960
|
userPaused = excluded.userPaused,
|
|
41487
41961
|
baseBranch = excluded.baseBranch,
|
|
41488
41962
|
branch = excluded.branch,
|
|
41963
|
+
autoMerge = excluded.autoMerge,
|
|
41489
41964
|
executionStartBranch = excluded.executionStartBranch,
|
|
41490
41965
|
baseCommitSha = excluded.baseCommitSha,
|
|
41491
41966
|
modelPresetId = excluded.modelPresetId,
|
|
@@ -41498,6 +41973,9 @@ ${outcome}`;
|
|
|
41498
41973
|
mergeRetries = excluded.mergeRetries,
|
|
41499
41974
|
workflowStepRetries = excluded.workflowStepRetries,
|
|
41500
41975
|
stuckKillCount = excluded.stuckKillCount,
|
|
41976
|
+
resumeLimboCount = excluded.resumeLimboCount,
|
|
41977
|
+
resumeLimboTipSha = excluded.resumeLimboTipSha,
|
|
41978
|
+
resumeLimboStepSignature = excluded.resumeLimboStepSignature,
|
|
41501
41979
|
postReviewFixCount = excluded.postReviewFixCount,
|
|
41502
41980
|
recoveryRetryCount = excluded.recoveryRetryCount,
|
|
41503
41981
|
taskDoneRetryCount = excluded.taskDoneRetryCount,
|
|
@@ -41506,6 +41984,7 @@ ${outcome}`;
|
|
|
41506
41984
|
verificationFailureCount = excluded.verificationFailureCount,
|
|
41507
41985
|
mergeConflictBounceCount = excluded.mergeConflictBounceCount,
|
|
41508
41986
|
mergeAuditBounceCount = excluded.mergeAuditBounceCount,
|
|
41987
|
+
mergeTransientRetryCount = excluded.mergeTransientRetryCount,
|
|
41509
41988
|
branchConflictRecoveryCount = excluded.branchConflictRecoveryCount,
|
|
41510
41989
|
reviewerContextRetryCount = excluded.reviewerContextRetryCount,
|
|
41511
41990
|
reviewerFallbackRetryCount = excluded.reviewerFallbackRetryCount,
|
|
@@ -41552,6 +42031,7 @@ ${outcome}`;
|
|
|
41552
42031
|
mergeDetails = excluded.mergeDetails,
|
|
41553
42032
|
breakIntoSubtasks = excluded.breakIntoSubtasks,
|
|
41554
42033
|
noCommitsExpected = excluded.noCommitsExpected,
|
|
42034
|
+
autoMerge = excluded.autoMerge,
|
|
41555
42035
|
enabledWorkflowSteps = excluded.enabledWorkflowSteps,
|
|
41556
42036
|
modifiedFiles = excluded.modifiedFiles,
|
|
41557
42037
|
missionId = excluded.missionId,
|
|
@@ -42827,6 +43307,7 @@ ${outcome}`;
|
|
|
42827
43307
|
sourceParentTaskId: input.source?.sourceParentTaskId,
|
|
42828
43308
|
sourceMetadata: withTaskBranchContextInSourceMetadata(input.source?.sourceMetadata, input.branchContext),
|
|
42829
43309
|
branchContext: input.branchContext,
|
|
43310
|
+
autoMerge: input.autoMerge,
|
|
42830
43311
|
column: input.column || "triage",
|
|
42831
43312
|
dependencies: input.dependencies || [],
|
|
42832
43313
|
breakIntoSubtasks: input.breakIntoSubtasks === true ? true : void 0,
|
|
@@ -43169,6 +43650,99 @@ ${newTask.description}
|
|
|
43169
43650
|
return { ...task, prompt };
|
|
43170
43651
|
});
|
|
43171
43652
|
}
|
|
43653
|
+
createBranchGroup(input) {
|
|
43654
|
+
const now = Date.now();
|
|
43655
|
+
const id = this.generateBranchGroupId();
|
|
43656
|
+
this.db.prepare(`
|
|
43657
|
+
INSERT INTO branch_groups (id, sourceType, sourceId, branchName, worktreePath, autoMerge, prState, prUrl, prNumber, status, createdAt, updatedAt, closedAt)
|
|
43658
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
43659
|
+
`).run(
|
|
43660
|
+
id,
|
|
43661
|
+
input.sourceType,
|
|
43662
|
+
input.sourceId,
|
|
43663
|
+
input.branchName,
|
|
43664
|
+
input.worktreePath ?? null,
|
|
43665
|
+
input.autoMerge ? 1 : 0,
|
|
43666
|
+
input.prState ?? "none",
|
|
43667
|
+
input.prUrl ?? null,
|
|
43668
|
+
input.prNumber ?? null,
|
|
43669
|
+
input.status ?? "open",
|
|
43670
|
+
now,
|
|
43671
|
+
now,
|
|
43672
|
+
input.closedAt ?? null
|
|
43673
|
+
);
|
|
43674
|
+
this.db.bumpLastModified();
|
|
43675
|
+
return this.getBranchGroup(id);
|
|
43676
|
+
}
|
|
43677
|
+
getBranchGroup(id) {
|
|
43678
|
+
const row = this.db.prepare(`SELECT * FROM branch_groups WHERE id = ?`).get(id);
|
|
43679
|
+
return row ? this.rowToBranchGroup(row) : null;
|
|
43680
|
+
}
|
|
43681
|
+
getBranchGroupBySource(sourceType, sourceId) {
|
|
43682
|
+
const row = this.db.prepare(`SELECT * FROM branch_groups WHERE sourceType = ? AND sourceId = ?`).get(sourceType, sourceId);
|
|
43683
|
+
return row ? this.rowToBranchGroup(row) : null;
|
|
43684
|
+
}
|
|
43685
|
+
listBranchGroups(options) {
|
|
43686
|
+
const rows = options?.status ? this.db.prepare(`SELECT * FROM branch_groups WHERE status = ? ORDER BY createdAt ASC`).all(options.status) : this.db.prepare(`SELECT * FROM branch_groups ORDER BY createdAt ASC`).all();
|
|
43687
|
+
return rows.map((row) => this.rowToBranchGroup(row));
|
|
43688
|
+
}
|
|
43689
|
+
updateBranchGroup(id, patch) {
|
|
43690
|
+
const current = this.getBranchGroup(id);
|
|
43691
|
+
if (!current) {
|
|
43692
|
+
throw new Error(`Branch group ${id} not found`);
|
|
43693
|
+
}
|
|
43694
|
+
const nextStatus = patch.status ?? current.status;
|
|
43695
|
+
const now = Date.now();
|
|
43696
|
+
const nextClosedAt = patch.closedAt === null ? null : patch.closedAt ?? (nextStatus !== "open" && current.status === "open" ? now : current.closedAt ?? null);
|
|
43697
|
+
this.db.prepare(`
|
|
43698
|
+
UPDATE branch_groups
|
|
43699
|
+
SET sourceId = ?, branchName = ?, worktreePath = ?, autoMerge = ?, prState = ?, prUrl = ?, prNumber = ?, status = ?, updatedAt = ?, closedAt = ?
|
|
43700
|
+
WHERE id = ?
|
|
43701
|
+
`).run(
|
|
43702
|
+
patch.sourceId ?? current.sourceId,
|
|
43703
|
+
patch.branchName ?? current.branchName,
|
|
43704
|
+
patch.worktreePath === null ? null : patch.worktreePath ?? current.worktreePath ?? null,
|
|
43705
|
+
patch.autoMerge === void 0 ? current.autoMerge ? 1 : 0 : patch.autoMerge ? 1 : 0,
|
|
43706
|
+
patch.prState ?? current.prState,
|
|
43707
|
+
patch.prUrl === null ? null : patch.prUrl ?? current.prUrl ?? null,
|
|
43708
|
+
patch.prNumber === null ? null : patch.prNumber ?? current.prNumber ?? null,
|
|
43709
|
+
nextStatus,
|
|
43710
|
+
now,
|
|
43711
|
+
nextClosedAt,
|
|
43712
|
+
id
|
|
43713
|
+
);
|
|
43714
|
+
this.db.bumpLastModified();
|
|
43715
|
+
return this.getBranchGroup(id);
|
|
43716
|
+
}
|
|
43717
|
+
async setTaskBranchGroup(taskId, branchGroupId) {
|
|
43718
|
+
await this.withTaskLock(taskId, async () => {
|
|
43719
|
+
const dir = this.taskDir(taskId);
|
|
43720
|
+
const task = await this.readTaskJson(dir);
|
|
43721
|
+
let branchContext;
|
|
43722
|
+
if (branchGroupId) {
|
|
43723
|
+
const group = this.getBranchGroup(branchGroupId);
|
|
43724
|
+
if (!group) {
|
|
43725
|
+
throw new Error(`Branch group ${branchGroupId} not found`);
|
|
43726
|
+
}
|
|
43727
|
+
branchContext = {
|
|
43728
|
+
groupId: group.id,
|
|
43729
|
+
source: group.sourceType,
|
|
43730
|
+
assignmentMode: "shared"
|
|
43731
|
+
};
|
|
43732
|
+
}
|
|
43733
|
+
task.branchContext = branchContext;
|
|
43734
|
+
task.sourceMetadata = withTaskBranchContextInSourceMetadata(task.sourceMetadata, branchContext);
|
|
43735
|
+
if (!branchContext && task.sourceMetadata) {
|
|
43736
|
+
const nextSourceMetadata = { ...task.sourceMetadata };
|
|
43737
|
+
delete nextSourceMetadata[TASK_BRANCH_CONTEXT_METADATA_KEY];
|
|
43738
|
+
task.sourceMetadata = Object.keys(nextSourceMetadata).length > 0 ? nextSourceMetadata : void 0;
|
|
43739
|
+
}
|
|
43740
|
+
task.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
43741
|
+
await this.atomicWriteTaskJson(dir, task);
|
|
43742
|
+
if (this.isWatching) this.taskCache.set(taskId, { ...task });
|
|
43743
|
+
this.emit("task:updated", task);
|
|
43744
|
+
});
|
|
43745
|
+
}
|
|
43172
43746
|
async getTaskColumns(ids) {
|
|
43173
43747
|
if (ids.length === 0) {
|
|
43174
43748
|
return /* @__PURE__ */ new Map();
|
|
@@ -43893,6 +44467,7 @@ ${newTask.description}
|
|
|
43893
44467
|
throw new Error(`Cannot move ${id} to done: ${mergeBlocker}`);
|
|
43894
44468
|
}
|
|
43895
44469
|
}
|
|
44470
|
+
const settingsForInReview = toColumn === "in-review" && task.autoMerge === void 0 ? await this.getSettingsFast() : void 0;
|
|
43896
44471
|
const movedAt = internal.now ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
43897
44472
|
task.column = toColumn;
|
|
43898
44473
|
task.columnMovedAt = movedAt;
|
|
@@ -43952,6 +44527,9 @@ ${newTask.description}
|
|
|
43952
44527
|
}
|
|
43953
44528
|
}
|
|
43954
44529
|
if (toColumn === "in-review") {
|
|
44530
|
+
if (task.autoMerge === void 0 && settingsForInReview) {
|
|
44531
|
+
task.autoMerge = settingsForInReview.autoMerge;
|
|
44532
|
+
}
|
|
43955
44533
|
task.recoveryRetryCount = void 0;
|
|
43956
44534
|
task.nextRecoveryAt = void 0;
|
|
43957
44535
|
if (task.status === "queued") {
|
|
@@ -44335,11 +44913,21 @@ ${newTask.description}
|
|
|
44335
44913
|
} else if (updates.baseBranch !== void 0) {
|
|
44336
44914
|
task.baseBranch = updates.baseBranch;
|
|
44337
44915
|
}
|
|
44916
|
+
if (updates.autoMerge === null) {
|
|
44917
|
+
task.autoMerge = void 0;
|
|
44918
|
+
} else if (updates.autoMerge !== void 0) {
|
|
44919
|
+
task.autoMerge = updates.autoMerge;
|
|
44920
|
+
}
|
|
44338
44921
|
if (updates.branch === null) {
|
|
44339
44922
|
task.branch = void 0;
|
|
44340
44923
|
} else if (updates.branch !== void 0) {
|
|
44341
44924
|
task.branch = updates.branch;
|
|
44342
44925
|
}
|
|
44926
|
+
if (updates.autoMerge === null) {
|
|
44927
|
+
task.autoMerge = void 0;
|
|
44928
|
+
} else if (updates.autoMerge !== void 0) {
|
|
44929
|
+
task.autoMerge = updates.autoMerge;
|
|
44930
|
+
}
|
|
44343
44931
|
if (updates.executionStartBranch === null) {
|
|
44344
44932
|
task.executionStartBranch = void 0;
|
|
44345
44933
|
} else if (updates.executionStartBranch !== void 0) {
|
|
@@ -44359,6 +44947,21 @@ ${newTask.description}
|
|
|
44359
44947
|
} else if (updates.stuckKillCount !== void 0) {
|
|
44360
44948
|
task.stuckKillCount = updates.stuckKillCount;
|
|
44361
44949
|
}
|
|
44950
|
+
if (updates.resumeLimboCount === null) {
|
|
44951
|
+
task.resumeLimboCount = void 0;
|
|
44952
|
+
} else if (updates.resumeLimboCount !== void 0) {
|
|
44953
|
+
task.resumeLimboCount = updates.resumeLimboCount;
|
|
44954
|
+
}
|
|
44955
|
+
if (updates.resumeLimboTipSha === null) {
|
|
44956
|
+
task.resumeLimboTipSha = void 0;
|
|
44957
|
+
} else if (updates.resumeLimboTipSha !== void 0) {
|
|
44958
|
+
task.resumeLimboTipSha = updates.resumeLimboTipSha;
|
|
44959
|
+
}
|
|
44960
|
+
if (updates.resumeLimboStepSignature === null) {
|
|
44961
|
+
task.resumeLimboStepSignature = void 0;
|
|
44962
|
+
} else if (updates.resumeLimboStepSignature !== void 0) {
|
|
44963
|
+
task.resumeLimboStepSignature = updates.resumeLimboStepSignature;
|
|
44964
|
+
}
|
|
44362
44965
|
if (updates.postReviewFixCount === null) {
|
|
44363
44966
|
task.postReviewFixCount = void 0;
|
|
44364
44967
|
} else if (updates.postReviewFixCount !== void 0) {
|
|
@@ -44399,6 +45002,11 @@ ${newTask.description}
|
|
|
44399
45002
|
} else if (updates.mergeAuditBounceCount !== void 0) {
|
|
44400
45003
|
task.mergeAuditBounceCount = updates.mergeAuditBounceCount;
|
|
44401
45004
|
}
|
|
45005
|
+
if (updates.mergeTransientRetryCount === null) {
|
|
45006
|
+
task.mergeTransientRetryCount = void 0;
|
|
45007
|
+
} else if (updates.mergeTransientRetryCount !== void 0) {
|
|
45008
|
+
task.mergeTransientRetryCount = updates.mergeTransientRetryCount;
|
|
45009
|
+
}
|
|
44402
45010
|
if (updates.branchConflictRecoveryCount === null) {
|
|
44403
45011
|
task.branchConflictRecoveryCount = void 0;
|
|
44404
45012
|
} else if (updates.branchConflictRecoveryCount !== void 0) {
|
|
@@ -46450,8 +47058,42 @@ ${newTask.description}
|
|
|
46450
47058
|
INSERT INTO agentLogEntries (taskId, timestamp, text, type, detail, agent)
|
|
46451
47059
|
VALUES (?, ?, ?, ?, ?, ?)
|
|
46452
47060
|
`);
|
|
47061
|
+
const citationInputs = [];
|
|
46453
47062
|
for (const entry of validEntries) {
|
|
46454
|
-
stmt.run(
|
|
47063
|
+
const insertResult = stmt.run(
|
|
47064
|
+
entry.taskId,
|
|
47065
|
+
entry.timestamp,
|
|
47066
|
+
entry.text,
|
|
47067
|
+
entry.type,
|
|
47068
|
+
entry.detail,
|
|
47069
|
+
entry.agent
|
|
47070
|
+
);
|
|
47071
|
+
const insertedId = insertResult.lastInsertRowid;
|
|
47072
|
+
if (insertedId === void 0 || insertedId === null) {
|
|
47073
|
+
continue;
|
|
47074
|
+
}
|
|
47075
|
+
const sourceRef = `agentLog:${String(insertedId)}`;
|
|
47076
|
+
try {
|
|
47077
|
+
citationInputs.push(
|
|
47078
|
+
...this.scanAndRecordCitations(
|
|
47079
|
+
entry.text,
|
|
47080
|
+
"agent_log",
|
|
47081
|
+
sourceRef,
|
|
47082
|
+
entry.agent ?? "unknown",
|
|
47083
|
+
entry.taskId,
|
|
47084
|
+
entry.timestamp
|
|
47085
|
+
)
|
|
47086
|
+
);
|
|
47087
|
+
} catch (err) {
|
|
47088
|
+
console.warn("[fusion] Failed to scan goal citations from agent_log:", err);
|
|
47089
|
+
}
|
|
47090
|
+
}
|
|
47091
|
+
if (citationInputs.length > 0) {
|
|
47092
|
+
try {
|
|
47093
|
+
this.recordGoalCitations(citationInputs);
|
|
47094
|
+
} catch (err) {
|
|
47095
|
+
console.warn("[fusion] Failed to record goal citations from agent_log batch:", err);
|
|
47096
|
+
}
|
|
46455
47097
|
}
|
|
46456
47098
|
this.db.bumpLastModified();
|
|
46457
47099
|
}
|
|
@@ -46489,8 +47131,9 @@ ${newTask.description}
|
|
|
46489
47131
|
VALUES (?, ?, ?, ?, ?, ?)
|
|
46490
47132
|
`);
|
|
46491
47133
|
this.db.transaction(() => {
|
|
47134
|
+
const citationInputs = [];
|
|
46492
47135
|
for (const entry of normalizedEntries) {
|
|
46493
|
-
stmt.run(
|
|
47136
|
+
const insertResult = stmt.run(
|
|
46494
47137
|
entry.taskId,
|
|
46495
47138
|
timestamp,
|
|
46496
47139
|
entry.text,
|
|
@@ -46498,6 +47141,31 @@ ${newTask.description}
|
|
|
46498
47141
|
entry.detail ?? null,
|
|
46499
47142
|
entry.agent ?? null
|
|
46500
47143
|
);
|
|
47144
|
+
const insertedId = insertResult.lastInsertRowid;
|
|
47145
|
+
if (insertedId === void 0 || insertedId === null) {
|
|
47146
|
+
continue;
|
|
47147
|
+
}
|
|
47148
|
+
try {
|
|
47149
|
+
citationInputs.push(
|
|
47150
|
+
...this.scanAndRecordCitations(
|
|
47151
|
+
entry.text,
|
|
47152
|
+
"agent_log",
|
|
47153
|
+
`agentLog:${String(insertedId)}`,
|
|
47154
|
+
entry.agent ?? "unknown",
|
|
47155
|
+
entry.taskId,
|
|
47156
|
+
timestamp
|
|
47157
|
+
)
|
|
47158
|
+
);
|
|
47159
|
+
} catch (err) {
|
|
47160
|
+
console.warn("[fusion] Failed to scan goal citations from agent log batch:", err);
|
|
47161
|
+
}
|
|
47162
|
+
}
|
|
47163
|
+
if (citationInputs.length > 0) {
|
|
47164
|
+
try {
|
|
47165
|
+
this.recordGoalCitations(citationInputs);
|
|
47166
|
+
} catch (err) {
|
|
47167
|
+
console.warn("[fusion] Failed to record goal citations from appendAgentLogBatch:", err);
|
|
47168
|
+
}
|
|
46501
47169
|
}
|
|
46502
47170
|
this.db.bumpLastModified();
|
|
46503
47171
|
});
|
|
@@ -46892,6 +47560,21 @@ ${newTask.description}
|
|
|
46892
47560
|
this.db.bumpLastModified();
|
|
46893
47561
|
const task = await this.getTask(taskId);
|
|
46894
47562
|
this.emit("task:updated", task);
|
|
47563
|
+
try {
|
|
47564
|
+
const citationInputs = this.scanAndRecordCitations(
|
|
47565
|
+
input.content,
|
|
47566
|
+
"task_document",
|
|
47567
|
+
`document:${taskId}:${input.key}:rev${document2.revision}`,
|
|
47568
|
+
input.author ?? "user",
|
|
47569
|
+
taskId,
|
|
47570
|
+
document2.updatedAt
|
|
47571
|
+
);
|
|
47572
|
+
if (citationInputs.length > 0) {
|
|
47573
|
+
this.recordGoalCitations(citationInputs);
|
|
47574
|
+
}
|
|
47575
|
+
} catch (err) {
|
|
47576
|
+
console.warn("[fusion] Failed to scan/record goal citations from task document:", err);
|
|
47577
|
+
}
|
|
46895
47578
|
return document2;
|
|
46896
47579
|
}
|
|
46897
47580
|
/**
|
|
@@ -48742,6 +49425,7 @@ var init_manual_retry_reset = __esm({
|
|
|
48742
49425
|
"use strict";
|
|
48743
49426
|
MANUAL_RETRY_RESET_COUNTER_KEYS = [
|
|
48744
49427
|
"stuckKillCount",
|
|
49428
|
+
"resumeLimboCount",
|
|
48745
49429
|
"recoveryRetryCount",
|
|
48746
49430
|
"taskDoneRetryCount",
|
|
48747
49431
|
"worktreeSessionRetryCount",
|
|
@@ -69676,6 +70360,8 @@ __export(src_exports, {
|
|
|
69676
70360
|
FileMemoryBackend: () => FileMemoryBackend,
|
|
69677
70361
|
FirstRunDetector: () => FirstRunDetector,
|
|
69678
70362
|
GLOBAL_SETTINGS_KEYS: () => GLOBAL_SETTINGS_KEYS,
|
|
70363
|
+
GOAL_CITATION_SNIPPET_MAX: () => GOAL_CITATION_SNIPPET_MAX,
|
|
70364
|
+
GOAL_ID_PATTERN: () => GOAL_ID_PATTERN,
|
|
69679
70365
|
GlobalSettingsStore: () => GlobalSettingsStore,
|
|
69680
70366
|
GoalStore: () => GoalStore,
|
|
69681
70367
|
HIGH_FANOUT_BLOCKER_TODO_THRESHOLD: () => HIGH_FANOUT_BLOCKER_TODO_THRESHOLD,
|
|
@@ -69846,6 +70532,7 @@ __export(src_exports, {
|
|
|
69846
70532
|
buildQmdSearchArgs: () => buildQmdSearchArgs,
|
|
69847
70533
|
buildResearchDocumentKey: () => buildResearchDocumentKey,
|
|
69848
70534
|
buildReviewerMemoryInstructions: () => buildReviewerMemoryInstructions,
|
|
70535
|
+
buildSnippet: () => buildSnippet,
|
|
69849
70536
|
buildTaskLineageTrailer: () => buildTaskLineageTrailer,
|
|
69850
70537
|
buildTriageMemoryInstructions: () => buildTriageMemoryInstructions,
|
|
69851
70538
|
buildUnblockWeightMap: () => buildUnblockWeightMap,
|
|
@@ -69927,6 +70614,7 @@ __export(src_exports, {
|
|
|
69927
70614
|
extractClaudeCliStoredCredential: () => extractClaudeCliStoredCredential,
|
|
69928
70615
|
extractCodexCliStoredCredential: () => extractCodexCliStoredCredential,
|
|
69929
70616
|
extractDreamProcessorResult: () => extractDreamProcessorResult,
|
|
70617
|
+
extractGoalCitations: () => extractGoalCitations,
|
|
69930
70618
|
extractIntentSignature: () => extractIntentSignature,
|
|
69931
70619
|
extractSandboxProvisioningRequest: () => extractSandboxProvisioningRequest,
|
|
69932
70620
|
extractTaskIdTokens: () => extractTaskIdTokens,
|
|
@@ -70120,6 +70808,7 @@ __export(src_exports, {
|
|
|
70120
70808
|
resolveAgentProvisioningPolicy: () => resolveAgentProvisioningPolicy,
|
|
70121
70809
|
resolveDependencyOrder: () => resolveDependencyOrder,
|
|
70122
70810
|
resolveEffectiveAgentPermissionPolicy: () => resolveEffectiveAgentPermissionPolicy,
|
|
70811
|
+
resolveEffectiveAutoMerge: () => resolveEffectiveAutoMerge,
|
|
70123
70812
|
resolveEvalSettings: () => resolveEvalSettings,
|
|
70124
70813
|
resolveExecutionSettingsModel: () => resolveExecutionSettingsModel,
|
|
70125
70814
|
resolveGlobalDir: () => resolveGlobalDir,
|
|
@@ -70320,6 +71009,7 @@ var init_src = __esm({
|
|
|
70320
71009
|
init_mission_store();
|
|
70321
71010
|
init_goal_types();
|
|
70322
71011
|
init_goal_store();
|
|
71012
|
+
init_goal_citation_extractor();
|
|
70323
71013
|
init_central_core();
|
|
70324
71014
|
init_central_db();
|
|
70325
71015
|
init_node_connection();
|
|
@@ -78875,12 +79565,16 @@ var init_agent_instructions = __esm({
|
|
|
78875
79565
|
|
|
78876
79566
|
// ../engine/src/prompt-layers.ts
|
|
78877
79567
|
function buildPromptLayers(input) {
|
|
78878
|
-
const { basePrompt, agentInstructions, memorySection, pluginContributions, performanceFeedback } = input;
|
|
79568
|
+
const { basePrompt, agentInstructions, memorySection, goalContext, pluginContributions, performanceFeedback } = input;
|
|
78879
79569
|
const dynamicParts = [];
|
|
78880
79570
|
const trimmedMemory = memorySection?.trim() ?? "";
|
|
78881
79571
|
if (trimmedMemory) {
|
|
78882
79572
|
dynamicParts.push(trimmedMemory);
|
|
78883
79573
|
}
|
|
79574
|
+
const trimmedGoalContext = goalContext?.trim() ?? "";
|
|
79575
|
+
if (trimmedGoalContext) {
|
|
79576
|
+
dynamicParts.push(trimmedGoalContext);
|
|
79577
|
+
}
|
|
78884
79578
|
const trimmedInstructions = agentInstructions?.trim() ?? "";
|
|
78885
79579
|
if (trimmedInstructions) {
|
|
78886
79580
|
dynamicParts.push(`## Custom Instructions
|
|
@@ -84208,7 +84902,7 @@ function isInsideWorktreesDir(rootDir, worktreePath, settings) {
|
|
|
84208
84902
|
return isInsideConfiguredWorktreesDir(rootDir, settings, worktreePath);
|
|
84209
84903
|
}
|
|
84210
84904
|
function deriveTaskIdFromBranch(branchName) {
|
|
84211
|
-
const match = branchName.match(/^fusion\/(fn-\d+)(?:-\d+)
|
|
84905
|
+
const match = branchName.match(/^fusion\/(fn-\d+)(?:-\d+)?(?:-[a-z0-9._-]+)*$/i);
|
|
84212
84906
|
return match ? match[1].toUpperCase() : branchName.toUpperCase();
|
|
84213
84907
|
}
|
|
84214
84908
|
async function scanIdleWorktrees(rootDir, store, settings) {
|
|
@@ -100416,6 +101110,12 @@ async function classifyOwnedLandedEvidenceForSelfHealing(rootDir, task, mergeTar
|
|
|
100416
101110
|
const { classifyOwnedLandedEvidence: classifyOwnedLandedEvidence2 } = await Promise.resolve().then(() => (init_merger(), merger_exports));
|
|
100417
101111
|
return classifyOwnedLandedEvidence2(rootDir, task, { mergeTargetBranch });
|
|
100418
101112
|
}
|
|
101113
|
+
function buildResumeLimboStepSignature(task) {
|
|
101114
|
+
return JSON.stringify({
|
|
101115
|
+
currentStep: task.currentStep ?? null,
|
|
101116
|
+
steps: Array.isArray(task.steps) ? task.steps.map((step) => step.status) : []
|
|
101117
|
+
});
|
|
101118
|
+
}
|
|
100419
101119
|
function formatRecoveryTimestamp(date = /* @__PURE__ */ new Date()) {
|
|
100420
101120
|
const pad = (value) => String(value).padStart(2, "0");
|
|
100421
101121
|
return `${date.getUTCFullYear()}${pad(date.getUTCMonth() + 1)}${pad(date.getUTCDate())}-${pad(date.getUTCHours())}${pad(date.getUTCMinutes())}${pad(date.getUTCSeconds())}`;
|
|
@@ -100630,7 +101330,7 @@ function isNoTaskDoneFailure2(task) {
|
|
|
100630
101330
|
function hasStepProgress2(task) {
|
|
100631
101331
|
return task.steps.some((step) => step.status !== "pending");
|
|
100632
101332
|
}
|
|
100633
|
-
var log20, worktreeMetadataReconcileLog, execAsync27, yieldEventLoop2, DONE_TASK_INTEGRITY_SWEEP_LIMIT, BOARD_STALL_NOTIFICATION_COOLDOWN_MS, DB_CORRUPTION_NOTIFICATION_COOLDOWN_MS, STALE_ACTIVE_BRANCH_EXECUTION_GRACE_MS, COMPLETION_HANDOFF_LIMBO_GRACE_MS, MAX_COMPLETION_HANDOFF_LIMBO_RECOVERIES, APPROVED_TRIAGE_RECOVERY_GRACE_MS, STARVED_REFINEMENT_RECOVERY_GRACE_MS, STARVED_PEER_PROGRESS_THRESHOLD, STARVED_REFINEMENT_ESCALATION_COOLDOWN_MS, ORPHANED_EXECUTION_RECOVERY_GRACE_MS, ACTIVE_MERGE_STATUSES2, NON_TERMINAL_STEP_STATUSES2, STRANDED_COMPLETED_TODO_ACTIVE_STATUSES, GHOST_REVIEW_PRESERVED_STATUSES, ORPHANED_WITH_WORKTREE_GRACE_MS, MAX_TASK_DONE_RETRIES, MAX_WORKTREE_SESSION_RETRIES, MAX_AUTO_MERGE_RETRIES, MAX_TRANSIENT_MERGE_RECOVERIES, MAX_STARVATION_DROPS, DEADLOCK_RECOVERY_COOLDOWN_MS, DEFAULT_STALE_MERGING_STATUS_MIN_AGE_MS, DEFAULT_STALE_MERGING_FANOUT_MIN_AGE_MS, DEFAULT_UNBACKED_MERGING_FANOUT_GRACE_MS, DURABLE_ERROR_RECOVERY_MAX_RETRIES, DURABLE_ERROR_RECOVERY_BASE_COOLDOWN_MS, DURABLE_ERROR_RECOVERY_MAX_COOLDOWN_MS, RUNNING_ON_INACTIVE_TASK_STALE_RUN_MS, SelfHealingManager;
|
|
101333
|
+
var log20, worktreeMetadataReconcileLog, execAsync27, yieldEventLoop2, DONE_TASK_INTEGRITY_SWEEP_LIMIT, BOARD_STALL_NOTIFICATION_COOLDOWN_MS, DB_CORRUPTION_NOTIFICATION_COOLDOWN_MS, STALE_ACTIVE_BRANCH_EXECUTION_GRACE_MS, COMPLETION_HANDOFF_LIMBO_GRACE_MS, MAX_COMPLETION_HANDOFF_LIMBO_RECOVERIES, MAX_NO_PROGRESS_RESUME_ATTEMPTS, APPROVED_TRIAGE_RECOVERY_GRACE_MS, STARVED_REFINEMENT_RECOVERY_GRACE_MS, STARVED_PEER_PROGRESS_THRESHOLD, STARVED_REFINEMENT_ESCALATION_COOLDOWN_MS, ORPHANED_EXECUTION_RECOVERY_GRACE_MS, ACTIVE_MERGE_STATUSES2, NON_TERMINAL_STEP_STATUSES2, STRANDED_COMPLETED_TODO_ACTIVE_STATUSES, GHOST_REVIEW_PRESERVED_STATUSES, ORPHANED_WITH_WORKTREE_GRACE_MS, MAX_TASK_DONE_RETRIES, MAX_WORKTREE_SESSION_RETRIES, MAX_AUTO_MERGE_RETRIES, MAX_TRANSIENT_MERGE_RECOVERIES, MAX_STARVATION_DROPS, DEADLOCK_RECOVERY_COOLDOWN_MS, DEFAULT_STALE_MERGING_STATUS_MIN_AGE_MS, DEFAULT_STALE_MERGING_FANOUT_MIN_AGE_MS, DEFAULT_UNBACKED_MERGING_FANOUT_GRACE_MS, DURABLE_ERROR_RECOVERY_MAX_RETRIES, DURABLE_ERROR_RECOVERY_BASE_COOLDOWN_MS, DURABLE_ERROR_RECOVERY_MAX_COOLDOWN_MS, RUNNING_ON_INACTIVE_TASK_STALE_RUN_MS, SelfHealingManager;
|
|
100634
101334
|
var init_self_healing = __esm({
|
|
100635
101335
|
"../engine/src/self-healing.ts"() {
|
|
100636
101336
|
"use strict";
|
|
@@ -100663,6 +101363,7 @@ var init_self_healing = __esm({
|
|
|
100663
101363
|
STALE_ACTIVE_BRANCH_EXECUTION_GRACE_MS = 10 * 6e4;
|
|
100664
101364
|
COMPLETION_HANDOFF_LIMBO_GRACE_MS = 5 * 6e4;
|
|
100665
101365
|
MAX_COMPLETION_HANDOFF_LIMBO_RECOVERIES = 3;
|
|
101366
|
+
MAX_NO_PROGRESS_RESUME_ATTEMPTS = 2;
|
|
100666
101367
|
APPROVED_TRIAGE_RECOVERY_GRACE_MS = 6e4;
|
|
100667
101368
|
STARVED_REFINEMENT_RECOVERY_GRACE_MS = 10 * 6e4;
|
|
100668
101369
|
STARVED_PEER_PROGRESS_THRESHOLD = 3;
|
|
@@ -102179,13 +102880,71 @@ var init_self_healing = __esm({
|
|
|
102179
102880
|
}
|
|
102180
102881
|
}
|
|
102181
102882
|
const preservedCommitCount = inspection.kind === "fully-subsumed" ? 0 : inspection.taskAttributedCommitCount;
|
|
102883
|
+
const stepSignature = buildResumeLimboStepSignature(task);
|
|
102884
|
+
const hasActiveSessionSignal = Boolean(task.checkedOutBy) || activeTaskIds.has(task.id.toUpperCase());
|
|
102885
|
+
const hasPriorSnapshot = typeof task.resumeLimboTipSha === "string" && typeof task.resumeLimboStepSignature === "string";
|
|
102886
|
+
const unchangedSincePriorResume = hasPriorSnapshot && task.resumeLimboTipSha === inspection.tipSha && task.resumeLimboStepSignature === stepSignature;
|
|
102887
|
+
const isNoProgressResume = task.column === "in-progress" && unchangedSincePriorResume && !hasActiveSessionSignal;
|
|
102888
|
+
const resumeAttemptCount = isNoProgressResume ? (task.resumeLimboCount ?? 0) + 1 : 0;
|
|
102889
|
+
if (task.column === "in-progress" && isNoProgressResume && resumeAttemptCount >= MAX_NO_PROGRESS_RESUME_ATTEMPTS) {
|
|
102890
|
+
const idleAnchor = task.executionStartedAt ?? task.columnMovedAt ?? task.updatedAt;
|
|
102891
|
+
const idleAnchorMs = Date.parse(idleAnchor ?? "");
|
|
102892
|
+
const idleMs = Number.isFinite(idleAnchorMs) ? Math.max(0, Date.now() - idleAnchorMs) : null;
|
|
102893
|
+
await this.store.moveTask(task.id, "todo", {
|
|
102894
|
+
moveSource: "engine",
|
|
102895
|
+
preserveWorktree: true,
|
|
102896
|
+
preserveProgress: true,
|
|
102897
|
+
preserveResumeState: true
|
|
102898
|
+
});
|
|
102899
|
+
await this.store.updateTask(task.id, {
|
|
102900
|
+
resumeLimboCount: 0,
|
|
102901
|
+
resumeLimboTipSha: inspection.tipSha,
|
|
102902
|
+
resumeLimboStepSignature: stepSignature
|
|
102903
|
+
});
|
|
102904
|
+
await this.store.logEntry(
|
|
102905
|
+
task.id,
|
|
102906
|
+
`[recovery] resume-limbo-escalated ${task.id} moved to todo after ${resumeAttemptCount} no-progress reclaim/resume attempts`,
|
|
102907
|
+
JSON.stringify({
|
|
102908
|
+
frozenTipSha: inspection.tipSha,
|
|
102909
|
+
idleMs,
|
|
102910
|
+
resumeAttemptCount,
|
|
102911
|
+
currentStep: task.currentStep ?? null
|
|
102912
|
+
})
|
|
102913
|
+
);
|
|
102914
|
+
try {
|
|
102915
|
+
await createRunAuditor(this.store, {
|
|
102916
|
+
runId: generateSyntheticRunId("self-heal", task.id),
|
|
102917
|
+
agentId: "self-healing",
|
|
102918
|
+
taskId: task.id,
|
|
102919
|
+
taskLineageId: task.lineageId,
|
|
102920
|
+
phase: "reclaim-self-owned-branch-conflicts"
|
|
102921
|
+
}).database({
|
|
102922
|
+
type: "task:resume-limbo-escalated",
|
|
102923
|
+
target: task.id,
|
|
102924
|
+
metadata: {
|
|
102925
|
+
taskId: task.id,
|
|
102926
|
+
frozenTipSha: inspection.tipSha,
|
|
102927
|
+
idleMs,
|
|
102928
|
+
resumeAttemptCount,
|
|
102929
|
+
currentStep: task.currentStep ?? null
|
|
102930
|
+
}
|
|
102931
|
+
});
|
|
102932
|
+
} catch (auditErr) {
|
|
102933
|
+
log20.warn(`Failed to write task:resume-limbo-escalated run-audit event for ${task.id}: ${auditErr instanceof Error ? auditErr.message : String(auditErr)}`);
|
|
102934
|
+
}
|
|
102935
|
+
recovered++;
|
|
102936
|
+
continue;
|
|
102937
|
+
}
|
|
102182
102938
|
await this.store.updateTask(task.id, {
|
|
102183
102939
|
worktree: inspection.livePath,
|
|
102184
102940
|
branch: task.branch,
|
|
102185
102941
|
paused: false,
|
|
102186
102942
|
pausedReason: void 0,
|
|
102187
102943
|
status: null,
|
|
102188
|
-
error: null
|
|
102944
|
+
error: null,
|
|
102945
|
+
resumeLimboCount: resumeAttemptCount,
|
|
102946
|
+
resumeLimboTipSha: inspection.tipSha,
|
|
102947
|
+
resumeLimboStepSignature: stepSignature
|
|
102189
102948
|
});
|
|
102190
102949
|
await this.store.logEntry(
|
|
102191
102950
|
task.id,
|
|
@@ -103417,7 +104176,7 @@ var init_self_healing = __esm({
|
|
|
103417
104176
|
const blockerId = task.blockedBy;
|
|
103418
104177
|
const unresolvedDeps = task.dependencies.filter((depId) => {
|
|
103419
104178
|
const dep = taskById.get(depId);
|
|
103420
|
-
return dep && dep.column !== "done" && dep.column !== "in-review" && dep.column !== "archived";
|
|
104179
|
+
return dep && !dep.deletedAt && dep.column !== "done" && dep.column !== "in-review" && dep.column !== "archived";
|
|
103421
104180
|
});
|
|
103422
104181
|
const overlapBlocker = task.overlapBlockedBy ? taskById.get(task.overlapBlockedBy) : void 0;
|
|
103423
104182
|
const hasActiveOverlapBlocker = Boolean(
|
|
@@ -103443,6 +104202,9 @@ var init_self_healing = __esm({
|
|
|
103443
104202
|
reasonCode = "missing-blocker";
|
|
103444
104203
|
reason = `blocker ${blockerId} missing`;
|
|
103445
104204
|
}
|
|
104205
|
+
} else if (blocker.deletedAt) {
|
|
104206
|
+
reasonCode = "soft-deleted-blocker";
|
|
104207
|
+
reason = `blocker ${blockerId} soft-deleted at ${blocker.deletedAt}`;
|
|
103446
104208
|
} else if (blocker.column === "done") {
|
|
103447
104209
|
reasonCode = "blocker-done";
|
|
103448
104210
|
reason = `blocker ${blockerId} is done`;
|
|
@@ -104972,6 +105734,12 @@ var init_self_healing = __esm({
|
|
|
104972
105734
|
try {
|
|
104973
105735
|
const hardBlocker = getTaskHardMergeBlocker({
|
|
104974
105736
|
...task,
|
|
105737
|
+
// Merge-confirmed tasks have already landed. Treat stale merge
|
|
105738
|
+
// in-flight statuses as soft state to clear during finalization,
|
|
105739
|
+
// not hard blockers that park an otherwise confirmed merge as failed.
|
|
105740
|
+
paused: false,
|
|
105741
|
+
status: task.status === "merging" || task.status === "merging-pr" ? void 0 : task.status,
|
|
105742
|
+
error: void 0,
|
|
104975
105743
|
steps: task.steps ?? [],
|
|
104976
105744
|
workflowStepResults: task.workflowStepResults
|
|
104977
105745
|
});
|
|
@@ -109603,6 +110371,258 @@ var init_token_cap_detector = __esm({
|
|
|
109603
110371
|
}
|
|
109604
110372
|
});
|
|
109605
110373
|
|
|
110374
|
+
// ../engine/src/goal-context-injector.ts
|
|
110375
|
+
function normalizeTitle(title) {
|
|
110376
|
+
return title.replace(/\s*\n+\s*/g, " ").trim();
|
|
110377
|
+
}
|
|
110378
|
+
function renderGoalContext(goals) {
|
|
110379
|
+
const lines = goals.map((goal) => `- ${goal.id}: ${normalizeTitle(goal.title)}`);
|
|
110380
|
+
return `## Active Goals
|
|
110381
|
+
|
|
110382
|
+
${lines.join("\n")}`;
|
|
110383
|
+
}
|
|
110384
|
+
function buildGoalContextSection(input) {
|
|
110385
|
+
const { activeGoals, onTruncated } = input;
|
|
110386
|
+
if (activeGoals.length === 0) {
|
|
110387
|
+
return { text: "", emittedGoalIds: [], truncated: null };
|
|
110388
|
+
}
|
|
110389
|
+
const sortedGoals = [...activeGoals].sort((a, b2) => {
|
|
110390
|
+
const byCreated = a.createdAt.localeCompare(b2.createdAt);
|
|
110391
|
+
if (byCreated !== 0) {
|
|
110392
|
+
return byCreated;
|
|
110393
|
+
}
|
|
110394
|
+
return a.id.localeCompare(b2.id);
|
|
110395
|
+
});
|
|
110396
|
+
const resolvedMaxGoals = Math.min(input.maxGoals ?? MAX_INJECTED_GOALS, MAX_INJECTED_GOALS);
|
|
110397
|
+
const charBudget = input.charBudget ?? DEFAULT_GOAL_INJECTION_CHAR_BUDGET;
|
|
110398
|
+
const droppedGoalIds = [];
|
|
110399
|
+
let selectedGoals = sortedGoals.slice(0, resolvedMaxGoals);
|
|
110400
|
+
if (sortedGoals.length > resolvedMaxGoals) {
|
|
110401
|
+
droppedGoalIds.push(...sortedGoals.slice(resolvedMaxGoals).map((goal) => goal.id));
|
|
110402
|
+
}
|
|
110403
|
+
let text = renderGoalContext(selectedGoals);
|
|
110404
|
+
let usedBudgetDrop = false;
|
|
110405
|
+
while (selectedGoals.length > 1 && text.length > charBudget) {
|
|
110406
|
+
const dropped = selectedGoals[selectedGoals.length - 1];
|
|
110407
|
+
selectedGoals = selectedGoals.slice(0, -1);
|
|
110408
|
+
droppedGoalIds.push(dropped.id);
|
|
110409
|
+
text = renderGoalContext(selectedGoals);
|
|
110410
|
+
usedBudgetDrop = true;
|
|
110411
|
+
}
|
|
110412
|
+
let truncated = null;
|
|
110413
|
+
if (droppedGoalIds.length > 0) {
|
|
110414
|
+
truncated = {
|
|
110415
|
+
reason: usedBudgetDrop ? "budget" : "cap",
|
|
110416
|
+
requested: activeGoals.length,
|
|
110417
|
+
emitted: selectedGoals.length,
|
|
110418
|
+
droppedGoalIds,
|
|
110419
|
+
charBudget,
|
|
110420
|
+
producedChars: text.length
|
|
110421
|
+
};
|
|
110422
|
+
onTruncated?.(truncated);
|
|
110423
|
+
}
|
|
110424
|
+
return {
|
|
110425
|
+
text,
|
|
110426
|
+
emittedGoalIds: selectedGoals.map((goal) => goal.id),
|
|
110427
|
+
truncated
|
|
110428
|
+
};
|
|
110429
|
+
}
|
|
110430
|
+
var MAX_INJECTED_GOALS, resolvedActiveGoalLimit, DEFAULT_GOAL_INJECTION_CHAR_BUDGET;
|
|
110431
|
+
var init_goal_context_injector = __esm({
|
|
110432
|
+
"../engine/src/goal-context-injector.ts"() {
|
|
110433
|
+
"use strict";
|
|
110434
|
+
init_src();
|
|
110435
|
+
MAX_INJECTED_GOALS = 5;
|
|
110436
|
+
resolvedActiveGoalLimit = "ACTIVE_GOAL_LIMIT" in src_exports && typeof ACTIVE_GOAL_LIMIT === "number" ? ACTIVE_GOAL_LIMIT : MAX_INJECTED_GOALS;
|
|
110437
|
+
if (MAX_INJECTED_GOALS > resolvedActiveGoalLimit) {
|
|
110438
|
+
throw new Error(
|
|
110439
|
+
`MAX_INJECTED_GOALS (${MAX_INJECTED_GOALS}) cannot exceed ACTIVE_GOAL_LIMIT (${resolvedActiveGoalLimit})`
|
|
110440
|
+
);
|
|
110441
|
+
}
|
|
110442
|
+
DEFAULT_GOAL_INJECTION_CHAR_BUDGET = 600;
|
|
110443
|
+
}
|
|
110444
|
+
});
|
|
110445
|
+
|
|
110446
|
+
// ../engine/src/goal-injection-diagnostics.ts
|
|
110447
|
+
function classifyGoalInjectionResult(result) {
|
|
110448
|
+
if (result.emittedGoalIds.length === 0) {
|
|
110449
|
+
return {
|
|
110450
|
+
outcome: "no-goals",
|
|
110451
|
+
goalCount: 0,
|
|
110452
|
+
goalIds: [],
|
|
110453
|
+
truncated: false
|
|
110454
|
+
};
|
|
110455
|
+
}
|
|
110456
|
+
return {
|
|
110457
|
+
outcome: "applied",
|
|
110458
|
+
goalCount: result.emittedGoalIds.length,
|
|
110459
|
+
goalIds: result.emittedGoalIds,
|
|
110460
|
+
truncated: result.truncated !== null
|
|
110461
|
+
};
|
|
110462
|
+
}
|
|
110463
|
+
function classifyGoalInjectionFailure(reason, error) {
|
|
110464
|
+
const resolvedErrorClass = error && typeof error === "object" && "constructor" in error && typeof error.constructor?.name === "string" ? error.constructor.name : void 0;
|
|
110465
|
+
return {
|
|
110466
|
+
outcome: "disabled-or-failed",
|
|
110467
|
+
goalCount: 0,
|
|
110468
|
+
goalIds: [],
|
|
110469
|
+
truncated: false,
|
|
110470
|
+
reason,
|
|
110471
|
+
...resolvedErrorClass ? { errorClass: resolvedErrorClass } : {}
|
|
110472
|
+
};
|
|
110473
|
+
}
|
|
110474
|
+
function resolveGoalContextForDiagnostics(input) {
|
|
110475
|
+
if (!input.listActiveGoals) {
|
|
110476
|
+
return {
|
|
110477
|
+
goalContext: "",
|
|
110478
|
+
classification: classifyGoalInjectionFailure(input.disabledReason ?? "store-unavailable")
|
|
110479
|
+
};
|
|
110480
|
+
}
|
|
110481
|
+
try {
|
|
110482
|
+
const activeGoals = input.listActiveGoals();
|
|
110483
|
+
const injector = input.injector ?? ((goals) => buildGoalContextSection({ activeGoals: goals }));
|
|
110484
|
+
try {
|
|
110485
|
+
const injectionResult = injector(activeGoals);
|
|
110486
|
+
return {
|
|
110487
|
+
goalContext: injectionResult.text,
|
|
110488
|
+
classification: classifyGoalInjectionResult(injectionResult)
|
|
110489
|
+
};
|
|
110490
|
+
} catch (injectorError) {
|
|
110491
|
+
return {
|
|
110492
|
+
goalContext: "",
|
|
110493
|
+
classification: classifyGoalInjectionFailure("injector-threw", injectorError)
|
|
110494
|
+
};
|
|
110495
|
+
}
|
|
110496
|
+
} catch (listError) {
|
|
110497
|
+
return {
|
|
110498
|
+
goalContext: "",
|
|
110499
|
+
classification: classifyGoalInjectionFailure("list-failed", listError)
|
|
110500
|
+
};
|
|
110501
|
+
}
|
|
110502
|
+
}
|
|
110503
|
+
function formatAgentLogLine(input) {
|
|
110504
|
+
const ids = JSON.stringify(input.goalIds);
|
|
110505
|
+
const reason = input.reason ? ` reason=${input.reason}` : "";
|
|
110506
|
+
const errorClass = input.errorClass ? ` err=${input.errorClass}` : "";
|
|
110507
|
+
return `[goal-injection] ${input.outcome} count=${input.goalCount} ids=${ids} truncated=${String(input.truncated)}${reason}${errorClass}`;
|
|
110508
|
+
}
|
|
110509
|
+
async function emitGoalInjectionDiagnostic(input) {
|
|
110510
|
+
const record = {
|
|
110511
|
+
lane: input.lane,
|
|
110512
|
+
outcome: input.outcome,
|
|
110513
|
+
goalCount: input.goalCount,
|
|
110514
|
+
goalIds: [...input.goalIds],
|
|
110515
|
+
truncated: input.truncated,
|
|
110516
|
+
...input.reason ? { reason: input.reason } : {},
|
|
110517
|
+
...input.errorClass ? { errorClass: input.errorClass } : {},
|
|
110518
|
+
...input.runId ? { runId: input.runId } : {},
|
|
110519
|
+
...input.agentId ? { agentId: input.agentId } : {},
|
|
110520
|
+
...input.taskId ? { taskId: input.taskId } : {},
|
|
110521
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
110522
|
+
};
|
|
110523
|
+
if (input.store && record.taskId) {
|
|
110524
|
+
try {
|
|
110525
|
+
await input.store.logEntry(record.taskId, formatAgentLogLine(record), void 0, input.runContext ?? void 0);
|
|
110526
|
+
} catch (error) {
|
|
110527
|
+
diagnosticsLog.warn(
|
|
110528
|
+
`failed to append goal-injection task log for ${record.taskId}: ${error instanceof Error ? error.message : String(error)}`
|
|
110529
|
+
);
|
|
110530
|
+
}
|
|
110531
|
+
}
|
|
110532
|
+
const auditStore = input.store;
|
|
110533
|
+
const hasAuditWriter = Boolean(auditStore && typeof auditStore.recordRunAuditEvent === "function");
|
|
110534
|
+
if (!input.runContext || !hasAuditWriter || !auditStore) {
|
|
110535
|
+
diagnosticsLog.warn(
|
|
110536
|
+
`goal-injection diagnostic emitted without run-audit side effect (lane=${record.lane}, hasContext=${Boolean(input.runContext)}, hasWriter=${Boolean(hasAuditWriter)})`
|
|
110537
|
+
);
|
|
110538
|
+
return record;
|
|
110539
|
+
}
|
|
110540
|
+
try {
|
|
110541
|
+
await auditStore.recordRunAuditEvent({
|
|
110542
|
+
taskId: input.runContext.taskId,
|
|
110543
|
+
agentId: input.runContext.agentId,
|
|
110544
|
+
runId: input.runContext.runId,
|
|
110545
|
+
domain: "database",
|
|
110546
|
+
mutationType: "prompt:goal-injection",
|
|
110547
|
+
target: record.lane,
|
|
110548
|
+
metadata: {
|
|
110549
|
+
lane: record.lane,
|
|
110550
|
+
outcome: record.outcome,
|
|
110551
|
+
goalCount: record.goalCount,
|
|
110552
|
+
goalIds: record.goalIds,
|
|
110553
|
+
truncated: record.truncated,
|
|
110554
|
+
...record.reason ? { reason: record.reason } : {},
|
|
110555
|
+
...record.errorClass ? { errorClass: record.errorClass } : {},
|
|
110556
|
+
...record.runId ? { runId: record.runId } : {},
|
|
110557
|
+
...record.agentId ? { agentId: record.agentId } : {},
|
|
110558
|
+
...record.taskId ? { taskId: record.taskId } : {}
|
|
110559
|
+
}
|
|
110560
|
+
});
|
|
110561
|
+
} catch (error) {
|
|
110562
|
+
diagnosticsLog.warn(
|
|
110563
|
+
`failed to append goal-injection run-audit event for lane=${record.lane}: ${error instanceof Error ? error.message : String(error)}`
|
|
110564
|
+
);
|
|
110565
|
+
}
|
|
110566
|
+
return record;
|
|
110567
|
+
}
|
|
110568
|
+
var diagnosticsLog;
|
|
110569
|
+
var init_goal_injection_diagnostics = __esm({
|
|
110570
|
+
"../engine/src/goal-injection-diagnostics.ts"() {
|
|
110571
|
+
"use strict";
|
|
110572
|
+
init_goal_context_injector();
|
|
110573
|
+
init_logger2();
|
|
110574
|
+
diagnosticsLog = createLogger2("goal-injection-diagnostics");
|
|
110575
|
+
}
|
|
110576
|
+
});
|
|
110577
|
+
|
|
110578
|
+
// ../engine/src/goal-anchoring-audit.ts
|
|
110579
|
+
async function emitGoalAnchoringAudit(auditor, input) {
|
|
110580
|
+
const isApplied = input.goalsInjected > 0;
|
|
110581
|
+
await auditor.database({
|
|
110582
|
+
type: isApplied ? GOAL_INJECTION_APPLIED : GOAL_INJECTION_SKIPPED,
|
|
110583
|
+
target: input.taskId ?? "goals",
|
|
110584
|
+
metadata: {
|
|
110585
|
+
lane: input.lane,
|
|
110586
|
+
count: input.goalsInjected,
|
|
110587
|
+
...typeof input.truncated === "boolean" ? { truncated: input.truncated } : {},
|
|
110588
|
+
...input.reason ? { reason: input.reason } : {}
|
|
110589
|
+
}
|
|
110590
|
+
});
|
|
110591
|
+
}
|
|
110592
|
+
function emitGoalRetrievalAudit(store, ctx, input) {
|
|
110593
|
+
if (!ctx.runId || !ctx.agentId) return;
|
|
110594
|
+
try {
|
|
110595
|
+
store.recordRunAuditEvent({
|
|
110596
|
+
runId: ctx.runId,
|
|
110597
|
+
agentId: ctx.agentId,
|
|
110598
|
+
taskId: ctx.taskId,
|
|
110599
|
+
domain: "database",
|
|
110600
|
+
mutationType: GOAL_RETRIEVAL_INVOKED,
|
|
110601
|
+
target: input.goalId ?? "goals",
|
|
110602
|
+
metadata: {
|
|
110603
|
+
toolName: input.toolName,
|
|
110604
|
+
count: input.resultCount,
|
|
110605
|
+
notFound: input.notFound ?? false
|
|
110606
|
+
}
|
|
110607
|
+
});
|
|
110608
|
+
} catch (error) {
|
|
110609
|
+
console.warn("[fusion-extension] goal retrieval audit emission skipped", error);
|
|
110610
|
+
}
|
|
110611
|
+
}
|
|
110612
|
+
var GOAL_INJECTION_APPLIED, GOAL_INJECTION_SKIPPED, GOAL_RETRIEVAL_INVOKED, GOAL_AUDIT_TYPES;
|
|
110613
|
+
var init_goal_anchoring_audit = __esm({
|
|
110614
|
+
"../engine/src/goal-anchoring-audit.ts"() {
|
|
110615
|
+
"use strict";
|
|
110616
|
+
GOAL_INJECTION_APPLIED = "goal:injection-applied";
|
|
110617
|
+
GOAL_INJECTION_SKIPPED = "goal:injection-skipped";
|
|
110618
|
+
GOAL_RETRIEVAL_INVOKED = "goal:retrieval-invoked";
|
|
110619
|
+
GOAL_AUDIT_TYPES = [GOAL_INJECTION_APPLIED, GOAL_INJECTION_SKIPPED, GOAL_RETRIEVAL_INVOKED];
|
|
110620
|
+
if (new Set(GOAL_AUDIT_TYPES).size !== GOAL_AUDIT_TYPES.length || GOAL_AUDIT_TYPES.some((value) => typeof value !== "string" || !value.startsWith("goal:"))) {
|
|
110621
|
+
throw new Error("Goal anchoring audit mutation types must be unique goal:* strings.");
|
|
110622
|
+
}
|
|
110623
|
+
}
|
|
110624
|
+
});
|
|
110625
|
+
|
|
109606
110626
|
// ../engine/src/auto-recovery-handlers/branch-worktree.ts
|
|
109607
110627
|
import { exec as exec31 } from "node:child_process";
|
|
109608
110628
|
import { existsSync as existsSync39 } from "node:fs";
|
|
@@ -110467,7 +111487,7 @@ function isTransientMissingTaskJsonError(error, task) {
|
|
|
110467
111487
|
const normalizedTaskJsonPath = resolvePath(filePath);
|
|
110468
111488
|
return normalizedTaskJsonPath.startsWith(`${normalizedWorktree}/`);
|
|
110469
111489
|
}
|
|
110470
|
-
function detectPendingReviewBlock(task,
|
|
111490
|
+
function detectPendingReviewBlock(task, _codeReviewVerdicts) {
|
|
110471
111491
|
const inProgressStepIndices = [];
|
|
110472
111492
|
for (let stepIndex = 0; stepIndex < task.steps.length; stepIndex++) {
|
|
110473
111493
|
if (task.steps[stepIndex]?.status === "in-progress") {
|
|
@@ -110479,9 +111499,6 @@ function detectPendingReviewBlock(task, codeReviewVerdicts) {
|
|
|
110479
111499
|
}
|
|
110480
111500
|
const recentActions = (task.log ?? []).slice(-30).map((entry) => entry.action?.trim()).filter((action) => Boolean(action));
|
|
110481
111501
|
for (const stepIndex of inProgressStepIndices) {
|
|
110482
|
-
if (codeReviewVerdicts.get(stepIndex) === "REVISE") {
|
|
110483
|
-
return { blocked: true, reason: "code-review-revise-outstanding", stepIndex };
|
|
110484
|
-
}
|
|
110485
111502
|
const stepDisplay = stepIndex + 1;
|
|
110486
111503
|
const codeRequest = `code review requested for Step ${stepDisplay}`;
|
|
110487
111504
|
const planRequest = `plan review requested for Step ${stepDisplay}`;
|
|
@@ -111042,6 +112059,8 @@ var init_executor = __esm({
|
|
|
111042
112059
|
init_worktree_hooks();
|
|
111043
112060
|
init_agent_instructions();
|
|
111044
112061
|
init_prompt_layers();
|
|
112062
|
+
init_goal_injection_diagnostics();
|
|
112063
|
+
init_goal_anchoring_audit();
|
|
111045
112064
|
init_run_audit();
|
|
111046
112065
|
init_auto_recovery();
|
|
111047
112066
|
init_restart_recovery_coordinator();
|
|
@@ -111428,6 +112447,11 @@ The tool prevents your session from being killed by the inactivity watchdog duri
|
|
|
111428
112447
|
executorLog.log(`Unpaused ${task.id} in-progress with no session \u2014 resuming execution`);
|
|
111429
112448
|
try {
|
|
111430
112449
|
await this.clearResumeFailureState(task);
|
|
112450
|
+
await this.store.updateTask(task.id, {
|
|
112451
|
+
resumeLimboCount: 0,
|
|
112452
|
+
resumeLimboTipSha: null,
|
|
112453
|
+
resumeLimboStepSignature: null
|
|
112454
|
+
});
|
|
111431
112455
|
await this.store.logEntry(task.id, "Resuming execution after unpause", void 0, this.getRunContextFor(task.id));
|
|
111432
112456
|
await this.recoverApprovedStepsOnResume(task.id);
|
|
111433
112457
|
} catch (clearErr) {
|
|
@@ -113803,8 +114827,30 @@ ${summary}`,
|
|
|
113803
114827
|
if (executorPluginContributions) {
|
|
113804
114828
|
executorLog.log(`${task.id}: applied plugin prompt contributions for executor-system surface`);
|
|
113805
114829
|
}
|
|
114830
|
+
const executorGoalResolution = resolveGoalContextForDiagnostics({
|
|
114831
|
+
listActiveGoals: typeof this.store.getGoalStore === "function" ? () => this.store.getGoalStore().listGoals({ status: "active" }) : void 0
|
|
114832
|
+
});
|
|
114833
|
+
const executorGoalContext = executorGoalResolution.goalContext;
|
|
114834
|
+
const executorGoalClassification = executorGoalResolution.classification;
|
|
114835
|
+
await emitGoalAnchoringAudit(audit, {
|
|
114836
|
+
lane: "executor",
|
|
114837
|
+
taskId: task.id,
|
|
114838
|
+
goalsInjected: executorGoalClassification.goalCount,
|
|
114839
|
+
truncated: executorGoalClassification.truncated,
|
|
114840
|
+
reason: executorGoalClassification.outcome === "no-goals" ? "no-active-goals" : void 0
|
|
114841
|
+
});
|
|
114842
|
+
await emitGoalInjectionDiagnostic({
|
|
114843
|
+
lane: "executor",
|
|
114844
|
+
...executorGoalClassification,
|
|
114845
|
+
runId: engineRunContext.runId,
|
|
114846
|
+
agentId: engineRunContext.agentId,
|
|
114847
|
+
taskId: task.id,
|
|
114848
|
+
store: this.store,
|
|
114849
|
+
runContext: engineRunContext
|
|
114850
|
+
});
|
|
113806
114851
|
const executorLayers = buildPromptLayers({
|
|
113807
114852
|
basePrompt: getExecutorSystemPrompt(settings),
|
|
114853
|
+
goalContext: executorGoalContext,
|
|
113808
114854
|
agentInstructions: executorInstructions,
|
|
113809
114855
|
pluginContributions: executorPluginContributions
|
|
113810
114856
|
});
|
|
@@ -119274,7 +120320,8 @@ async function collectTaskEvaluationEvidence(params) {
|
|
|
119274
120320
|
taskDoneRetryCount: task.taskDoneRetryCount ?? 0,
|
|
119275
120321
|
verificationFailureCount: task.verificationFailureCount ?? 0,
|
|
119276
120322
|
mergeConflictBounceCount: task.mergeConflictBounceCount ?? 0,
|
|
119277
|
-
mergeAuditBounceCount: task.mergeAuditBounceCount ?? 0
|
|
120323
|
+
mergeAuditBounceCount: task.mergeAuditBounceCount ?? 0,
|
|
120324
|
+
mergeTransientRetryCount: task.mergeTransientRetryCount ?? 0
|
|
119278
120325
|
}
|
|
119279
120326
|
}],
|
|
119280
120327
|
commits: commitEvidence,
|
|
@@ -119733,6 +120780,25 @@ function filterPathsByIgnoreList(paths, ignorePaths) {
|
|
|
119733
120780
|
}
|
|
119734
120781
|
return paths.filter((path6) => !normalizedIgnorePaths.some((ignore) => isIgnoredOverlapPath(path6, ignore)));
|
|
119735
120782
|
}
|
|
120783
|
+
function getUnmetSchedulingDependencies(task, tasks) {
|
|
120784
|
+
return task.dependencies.filter((depId) => {
|
|
120785
|
+
const dep = tasks.find((candidate) => candidate.id === depId);
|
|
120786
|
+
return dep && dep.column !== "done" && dep.column !== "in-review" && dep.column !== "archived";
|
|
120787
|
+
});
|
|
120788
|
+
}
|
|
120789
|
+
function isRunnableQueuedOverlapCandidate(task, tasks, now = Date.now(), activeScopes, scope = []) {
|
|
120790
|
+
if (task.column !== "todo" || task.status !== "queued") return false;
|
|
120791
|
+
if (task.paused || task.userPaused) return false;
|
|
120792
|
+
if (task.nextRecoveryAt && new Date(task.nextRecoveryAt).getTime() > now) return false;
|
|
120793
|
+
if (getUnmetSchedulingDependencies(task, tasks).length > 0) return false;
|
|
120794
|
+
if (!activeScopes || activeScopes.size === 0) return true;
|
|
120795
|
+
if (scope.length === 0) return true;
|
|
120796
|
+
for (const activeScope of activeScopes.values()) {
|
|
120797
|
+
if (!activeScope.length) continue;
|
|
120798
|
+
if (pathsOverlap2(scope, activeScope)) return false;
|
|
120799
|
+
}
|
|
120800
|
+
return true;
|
|
120801
|
+
}
|
|
119736
120802
|
function findHigherPriorityQueuedOverlap(candidate, queuedScopes, overlap) {
|
|
119737
120803
|
let higher = null;
|
|
119738
120804
|
for (const queued of queuedScopes) {
|
|
@@ -120407,34 +121473,47 @@ var init_scheduler = __esm({
|
|
|
120407
121473
|
);
|
|
120408
121474
|
}
|
|
120409
121475
|
const activeScopes = /* @__PURE__ */ new Map();
|
|
121476
|
+
const activeScopeColumns = /* @__PURE__ */ new Map();
|
|
121477
|
+
const setActiveScopeLease = (taskId, scope, column) => {
|
|
121478
|
+
activeScopes.set(taskId, scope);
|
|
121479
|
+
activeScopeColumns.set(taskId, column);
|
|
121480
|
+
};
|
|
120410
121481
|
const inversionEmitted = /* @__PURE__ */ new Set();
|
|
120411
121482
|
const queuedHigherPriorityScopes = [];
|
|
121483
|
+
const queuedHigherPriorityTaskById = /* @__PURE__ */ new Map();
|
|
121484
|
+
const overlapIgnorePaths = settings.overlapIgnorePaths ?? [];
|
|
121485
|
+
const filteredScopeByTaskId = /* @__PURE__ */ new Map();
|
|
121486
|
+
const getFilteredFileScope = async (taskId) => {
|
|
121487
|
+
const cached = filteredScopeByTaskId.get(taskId);
|
|
121488
|
+
if (cached) return cached;
|
|
121489
|
+
const scope = await this.store.parseFileScopeFromPrompt(taskId);
|
|
121490
|
+
const filteredScope = filterPathsByIgnoreList(scope, overlapIgnorePaths);
|
|
121491
|
+
filteredScopeByTaskId.set(taskId, filteredScope);
|
|
121492
|
+
return filteredScope;
|
|
121493
|
+
};
|
|
120412
121494
|
if (settings.groupOverlappingFiles) {
|
|
120413
|
-
const overlapIgnorePaths = settings.overlapIgnorePaths ?? [];
|
|
120414
121495
|
for (const t of inProgress) {
|
|
120415
|
-
const
|
|
120416
|
-
|
|
120417
|
-
|
|
121496
|
+
const filteredScope = await getFilteredFileScope(t.id);
|
|
121497
|
+
if (filteredScope.length > 0) setActiveScopeLease(t.id, filteredScope, "in-progress");
|
|
121498
|
+
}
|
|
121499
|
+
const inReviewWithWorktree = tasks.filter(
|
|
121500
|
+
(t) => t.column === "in-review" && Boolean(t.worktree) && !t.paused && t.status !== "failed"
|
|
121501
|
+
);
|
|
121502
|
+
for (const t of inReviewWithWorktree) {
|
|
121503
|
+
const filteredScope = await getFilteredFileScope(t.id);
|
|
121504
|
+
if (filteredScope.length > 0) setActiveScopeLease(t.id, filteredScope, "in-review");
|
|
120418
121505
|
}
|
|
120419
121506
|
for (const t of todo) {
|
|
120420
|
-
|
|
120421
|
-
const scope = await this.store.parseFileScopeFromPrompt(t.id);
|
|
120422
|
-
const filteredScope = filterPathsByIgnoreList(scope, overlapIgnorePaths);
|
|
121507
|
+
const filteredScope = await getFilteredFileScope(t.id);
|
|
120423
121508
|
if (filteredScope.length === 0) continue;
|
|
121509
|
+
if (!isRunnableQueuedOverlapCandidate(t, tasks, now, activeScopes, filteredScope)) continue;
|
|
120424
121510
|
queuedHigherPriorityScopes.push({
|
|
120425
121511
|
id: t.id,
|
|
120426
121512
|
priority: t.priority,
|
|
120427
121513
|
createdAt: t.createdAt,
|
|
120428
121514
|
scope: filteredScope
|
|
120429
121515
|
});
|
|
120430
|
-
|
|
120431
|
-
const inReviewWithWorktree = tasks.filter(
|
|
120432
|
-
(t) => t.column === "in-review" && Boolean(t.worktree) && !t.paused && t.status !== "failed"
|
|
120433
|
-
);
|
|
120434
|
-
for (const t of inReviewWithWorktree) {
|
|
120435
|
-
const scope = await this.store.parseFileScopeFromPrompt(t.id);
|
|
120436
|
-
const filteredScope = filterPathsByIgnoreList(scope, overlapIgnorePaths);
|
|
120437
|
-
if (filteredScope.length > 0) activeScopes.set(t.id, filteredScope);
|
|
121516
|
+
queuedHigherPriorityTaskById.set(t.id, t);
|
|
120438
121517
|
}
|
|
120439
121518
|
}
|
|
120440
121519
|
const ordered = resolveDependencyOrder(todo);
|
|
@@ -120455,10 +121534,7 @@ var init_scheduler = __esm({
|
|
|
120455
121534
|
continue;
|
|
120456
121535
|
}
|
|
120457
121536
|
}
|
|
120458
|
-
const unmetDeps = task
|
|
120459
|
-
const dep = tasks.find((t) => t.id === depId);
|
|
120460
|
-
return dep && dep.column !== "done" && dep.column !== "in-review" && dep.column !== "archived";
|
|
120461
|
-
});
|
|
121537
|
+
const unmetDeps = getUnmetSchedulingDependencies(task, tasks);
|
|
120462
121538
|
if (unmetDeps.length > 0) {
|
|
120463
121539
|
await this.store.updateTask(task.id, {
|
|
120464
121540
|
status: "queued",
|
|
@@ -120492,17 +121568,18 @@ var init_scheduler = __esm({
|
|
|
120492
121568
|
continue;
|
|
120493
121569
|
}
|
|
120494
121570
|
if (settings.groupOverlappingFiles) {
|
|
120495
|
-
const
|
|
120496
|
-
const taskScope = filterPathsByIgnoreList(
|
|
120497
|
-
await this.store.parseFileScopeFromPrompt(task.id),
|
|
120498
|
-
overlapIgnorePaths
|
|
120499
|
-
);
|
|
121571
|
+
const taskScope = await getFilteredFileScope(task.id);
|
|
120500
121572
|
if (taskScope.length > 0) {
|
|
120501
121573
|
const activeScopeEntries = Array.from(activeScopes.entries()).sort(([aId], [bId]) => aId.localeCompare(bId));
|
|
120502
121574
|
const overlapBlockerId = task.overlapBlockedBy || task.blockedBy;
|
|
120503
121575
|
const currentBlockerScope = overlapBlockerId ? activeScopes.get(overlapBlockerId) : void 0;
|
|
120504
121576
|
const hasValidCurrentBlocker = Boolean(overlapBlockerId) && Boolean(currentBlockerScope) && this.pathsOverlap(taskScope, currentBlockerScope);
|
|
120505
121577
|
const overlappingTaskId = hasValidCurrentBlocker ? overlapBlockerId : activeScopeEntries.find(([, ipScope]) => this.pathsOverlap(taskScope, ipScope))?.[0] ?? null;
|
|
121578
|
+
const runnableQueuedHigherPriorityScopes = queuedHigherPriorityScopes.filter((queuedCandidate) => {
|
|
121579
|
+
const queuedTask = queuedHigherPriorityTaskById.get(queuedCandidate.id);
|
|
121580
|
+
if (!queuedTask) return false;
|
|
121581
|
+
return isRunnableQueuedOverlapCandidate(queuedTask, tasks, now, activeScopes, queuedCandidate.scope);
|
|
121582
|
+
});
|
|
120506
121583
|
const higherPriorityQueuedOverlap = findHigherPriorityQueuedOverlap(
|
|
120507
121584
|
{
|
|
120508
121585
|
id: task.id,
|
|
@@ -120510,7 +121587,7 @@ var init_scheduler = __esm({
|
|
|
120510
121587
|
createdAt: task.createdAt,
|
|
120511
121588
|
scope: taskScope
|
|
120512
121589
|
},
|
|
120513
|
-
|
|
121590
|
+
runnableQueuedHigherPriorityScopes,
|
|
120514
121591
|
this.pathsOverlap.bind(this)
|
|
120515
121592
|
);
|
|
120516
121593
|
if (higherPriorityQueuedOverlap) {
|
|
@@ -120525,7 +121602,7 @@ var init_scheduler = __esm({
|
|
|
120525
121602
|
await this.rollbackRunningAgentsForQueuedTodoTask(task.id);
|
|
120526
121603
|
await this.logDispatchQueuedReason(
|
|
120527
121604
|
task.id,
|
|
120528
|
-
`queued \u2014 deferred for higher-priority queued task ${higherPriorityQueuedOverlap.id} (overlap)`
|
|
121605
|
+
`queued \u2014 deferred for higher-priority runnable queued task ${higherPriorityQueuedOverlap.id} (overlap)`
|
|
120529
121606
|
);
|
|
120530
121607
|
continue;
|
|
120531
121608
|
}
|
|
@@ -120557,7 +121634,7 @@ var init_scheduler = __esm({
|
|
|
120557
121634
|
blockerId: overlapBlockerTask.id,
|
|
120558
121635
|
blockerPriority: overlapBlockerTask.priority ?? null,
|
|
120559
121636
|
blockerCreatedAt: overlapBlockerTask.createdAt ?? null,
|
|
120560
|
-
blockerColumn: overlapBlockerTask.column,
|
|
121637
|
+
blockerColumn: activeScopeColumns.get(overlappingTaskId) ?? overlapBlockerTask.column,
|
|
120561
121638
|
source: "scheduler.overlap-priority-inversion"
|
|
120562
121639
|
}
|
|
120563
121640
|
});
|
|
@@ -120568,7 +121645,11 @@ var init_scheduler = __esm({
|
|
|
120568
121645
|
}
|
|
120569
121646
|
}
|
|
120570
121647
|
await this.rollbackRunningAgentsForQueuedTodoTask(task.id);
|
|
120571
|
-
|
|
121648
|
+
const activeLeaseColumn = activeScopeColumns.get(overlappingTaskId) ?? overlapBlockerTask?.column ?? "unknown";
|
|
121649
|
+
await this.logDispatchQueuedReason(
|
|
121650
|
+
task.id,
|
|
121651
|
+
`queued \u2014 blocked by active file-scope lease ${overlappingTaskId} (column=${activeLeaseColumn})`
|
|
121652
|
+
);
|
|
120572
121653
|
continue;
|
|
120573
121654
|
}
|
|
120574
121655
|
if (task.overlapBlockedBy) {
|
|
@@ -120787,11 +121868,8 @@ var init_scheduler = __esm({
|
|
|
120787
121868
|
this.options.onSchedule?.(task);
|
|
120788
121869
|
started++;
|
|
120789
121870
|
if (settings.groupOverlappingFiles) {
|
|
120790
|
-
const scope =
|
|
120791
|
-
|
|
120792
|
-
settings.overlapIgnorePaths
|
|
120793
|
-
);
|
|
120794
|
-
if (scope.length > 0) activeScopes.set(task.id, scope);
|
|
121871
|
+
const scope = await getFilteredFileScope(task.id);
|
|
121872
|
+
if (scope.length > 0) setActiveScopeLease(task.id, scope, "in-progress");
|
|
120795
121873
|
}
|
|
120796
121874
|
}
|
|
120797
121875
|
await this.emitHighOverlapFanoutWarnings(tasks);
|
|
@@ -123821,6 +124899,8 @@ var init_agent_heartbeat = __esm({
|
|
|
123821
124899
|
init_agent_instructions();
|
|
123822
124900
|
init_heartbeat_procedure_resolver();
|
|
123823
124901
|
init_prompt_layers();
|
|
124902
|
+
init_goal_injection_diagnostics();
|
|
124903
|
+
init_goal_anchoring_audit();
|
|
123824
124904
|
init_logger2();
|
|
123825
124905
|
init_worktree_acquisition();
|
|
123826
124906
|
init_run_audit();
|
|
@@ -125212,12 +126292,16 @@ Critical: a heartbeat without observable progress (or an explicit no-op reason)
|
|
|
125212
126292
|
engineRunContext.taskId = taskId;
|
|
125213
126293
|
}
|
|
125214
126294
|
let autoClaimCandidates = [];
|
|
126295
|
+
let autoClaimSnapshotCandidateCount = 0;
|
|
126296
|
+
let autoClaimRoleFilteredCount = 0;
|
|
125215
126297
|
const autoClaimEnabled = isAutoClaimRelevantTasksEnabled(agent);
|
|
125216
126298
|
if (!taskId && canRunNoTaskHeartbeat && autoClaimEnabled && this.snapshotManager) {
|
|
125217
126299
|
try {
|
|
125218
126300
|
const snapshot = await this.snapshotManager.getSnapshot();
|
|
126301
|
+
autoClaimSnapshotCandidateCount = snapshot.tasks.length;
|
|
125219
126302
|
const roleCompatibleCandidates = snapshot.tasks.filter((candidate) => canAgentTakeImplementationTask(agent, candidate));
|
|
125220
126303
|
const skippedIncompatibleCount = snapshot.tasks.length - roleCompatibleCandidates.length;
|
|
126304
|
+
autoClaimRoleFilteredCount = skippedIncompatibleCount;
|
|
125221
126305
|
if (skippedIncompatibleCount > 0) {
|
|
125222
126306
|
heartbeatLog.log(
|
|
125223
126307
|
`Agent ${agentId} (role=${agent.role}) skipped auto-claim of ${skippedIncompatibleCount} implementation task(s) \u2014 only executor agents may claim implementation work`
|
|
@@ -125473,8 +126557,29 @@ Critical: a heartbeat without observable progress (or an explicit no-op reason)
|
|
|
125473
126557
|
if (heartbeatPluginContributions) {
|
|
125474
126558
|
heartbeatLog.log(`applied plugin prompt contributions for heartbeat surface`);
|
|
125475
126559
|
}
|
|
126560
|
+
const heartbeatGoalResolution = resolveGoalContextForDiagnostics({
|
|
126561
|
+
listActiveGoals: typeof taskStore.getGoalStore === "function" ? () => taskStore.getGoalStore().listGoals({ status: "active" }) : void 0
|
|
126562
|
+
});
|
|
126563
|
+
const heartbeatGoalContext = heartbeatGoalResolution.goalContext;
|
|
126564
|
+
const heartbeatGoalClassification = heartbeatGoalResolution.classification;
|
|
126565
|
+
await emitGoalAnchoringAudit(audit, {
|
|
126566
|
+
lane: "heartbeat",
|
|
126567
|
+
taskId,
|
|
126568
|
+
goalsInjected: heartbeatGoalClassification.goalCount,
|
|
126569
|
+
truncated: heartbeatGoalClassification.truncated,
|
|
126570
|
+
reason: heartbeatGoalClassification.outcome === "no-goals" ? "no-active-goals" : void 0
|
|
126571
|
+
});
|
|
126572
|
+
await emitGoalInjectionDiagnostic({
|
|
126573
|
+
lane: "heartbeat",
|
|
126574
|
+
...heartbeatGoalClassification,
|
|
126575
|
+
runId: run.id,
|
|
126576
|
+
agentId,
|
|
126577
|
+
store: taskStore,
|
|
126578
|
+
runContext: engineRunContext
|
|
126579
|
+
});
|
|
125476
126580
|
const heartbeatLayers = buildPromptLayers({
|
|
125477
126581
|
basePrompt: baseHeartbeatSystemPrompt,
|
|
126582
|
+
goalContext: heartbeatGoalContext,
|
|
125478
126583
|
agentInstructions: [resolvedInstructionsText, memoryInstructions, selfImprovePrompt].filter((part) => part.trim()).join("\n\n"),
|
|
125479
126584
|
pluginContributions: heartbeatPluginContributions
|
|
125480
126585
|
});
|
|
@@ -125749,10 +126854,15 @@ Critical: a heartbeat without observable progress (or an explicit no-op reason)
|
|
|
125749
126854
|
);
|
|
125750
126855
|
}
|
|
125751
126856
|
const promptCandidateLimit = resolveAutoClaimCandidatesInPromptLimit(agent, heartbeatModelSettings);
|
|
126857
|
+
const autoClaimStatus = autoClaimEnabled ? promptCandidateLimit === 0 ? "disabled (prompt-suppressed)" : autoClaimCandidates.length === 0 && autoClaimSnapshotCandidateCount > 0 && autoClaimRoleFilteredCount > 0 ? "enabled (no role-compatible candidates; executor role required)" : "enabled" : "disabled";
|
|
126858
|
+
const noRoleCompatibleCandidateLines = autoClaimCandidates.length === 0 && autoClaimSnapshotCandidateCount > 0 && autoClaimRoleFilteredCount > 0 ? [
|
|
126859
|
+
`- Snapshot found ${autoClaimSnapshotCandidateCount} eligible Todo task(s), but this agent role cannot auto-claim implementation work.`,
|
|
126860
|
+
"- Backlog auto-claim is restricted to executor-role agents; use delegation or create coordination follow-up instead of assuming the board is empty."
|
|
126861
|
+
] : [];
|
|
125752
126862
|
const candidateLines = promptCandidateLimit > 0 ? [
|
|
125753
126863
|
"",
|
|
125754
126864
|
"Open Task Candidates (auto-claim scan):",
|
|
125755
|
-
...autoClaimCandidates.slice(0, promptCandidateLimit).map((candidate) => `- ${candidate.id}: ${candidate.title ?? candidate.descriptionFirstLine}`)
|
|
126865
|
+
...autoClaimCandidates.length > 0 ? autoClaimCandidates.slice(0, promptCandidateLimit).map((candidate) => `- ${candidate.id}: ${candidate.title ?? candidate.descriptionFirstLine}`) : noRoleCompatibleCandidateLines
|
|
125756
126866
|
] : [];
|
|
125757
126867
|
executionPrompt = [
|
|
125758
126868
|
`Heartbeat execution for agent "${agent.name}" (ID: ${agent.id})`,
|
|
@@ -125768,7 +126878,7 @@ Critical: a heartbeat without observable progress (or an explicit no-op reason)
|
|
|
125768
126878
|
...wakeTriggerSourceLine ? [wakeTriggerSourceLine] : [],
|
|
125769
126879
|
`- pending messages: ${pendingMessages.length}`,
|
|
125770
126880
|
`- pending room messages: ${pendingRoomMessages.total}`,
|
|
125771
|
-
`- auto-claim relevant tasks: ${
|
|
126881
|
+
`- auto-claim relevant tasks: ${autoClaimStatus}`,
|
|
125772
126882
|
"",
|
|
125773
126883
|
"Treat this wake delta as the highest-priority change for this heartbeat.",
|
|
125774
126884
|
"This is an autonomous heartbeat run (manual or automatic): re-anchor on",
|
|
@@ -127001,78 +128111,6 @@ var init_error_classifier = __esm({
|
|
|
127001
128111
|
}
|
|
127002
128112
|
});
|
|
127003
128113
|
|
|
127004
|
-
// ../engine/src/goal-context-injector.ts
|
|
127005
|
-
function normalizeTitle(title) {
|
|
127006
|
-
return title.replace(/\s*\n+\s*/g, " ").trim();
|
|
127007
|
-
}
|
|
127008
|
-
function renderGoalContext(goals) {
|
|
127009
|
-
const lines = goals.map((goal) => `- ${goal.id}: ${normalizeTitle(goal.title)}`);
|
|
127010
|
-
return `## Active Goals
|
|
127011
|
-
|
|
127012
|
-
${lines.join("\n")}`;
|
|
127013
|
-
}
|
|
127014
|
-
function buildGoalContextSection(input) {
|
|
127015
|
-
const { activeGoals, onTruncated } = input;
|
|
127016
|
-
if (activeGoals.length === 0) {
|
|
127017
|
-
return { text: "", emittedGoalIds: [], truncated: null };
|
|
127018
|
-
}
|
|
127019
|
-
const sortedGoals = [...activeGoals].sort((a, b2) => {
|
|
127020
|
-
const byCreated = a.createdAt.localeCompare(b2.createdAt);
|
|
127021
|
-
if (byCreated !== 0) {
|
|
127022
|
-
return byCreated;
|
|
127023
|
-
}
|
|
127024
|
-
return a.id.localeCompare(b2.id);
|
|
127025
|
-
});
|
|
127026
|
-
const resolvedMaxGoals = Math.min(input.maxGoals ?? MAX_INJECTED_GOALS, MAX_INJECTED_GOALS);
|
|
127027
|
-
const charBudget = input.charBudget ?? DEFAULT_GOAL_INJECTION_CHAR_BUDGET;
|
|
127028
|
-
const droppedGoalIds = [];
|
|
127029
|
-
let selectedGoals = sortedGoals.slice(0, resolvedMaxGoals);
|
|
127030
|
-
if (sortedGoals.length > resolvedMaxGoals) {
|
|
127031
|
-
droppedGoalIds.push(...sortedGoals.slice(resolvedMaxGoals).map((goal) => goal.id));
|
|
127032
|
-
}
|
|
127033
|
-
let text = renderGoalContext(selectedGoals);
|
|
127034
|
-
let usedBudgetDrop = false;
|
|
127035
|
-
while (selectedGoals.length > 1 && text.length > charBudget) {
|
|
127036
|
-
const dropped = selectedGoals[selectedGoals.length - 1];
|
|
127037
|
-
selectedGoals = selectedGoals.slice(0, -1);
|
|
127038
|
-
droppedGoalIds.push(dropped.id);
|
|
127039
|
-
text = renderGoalContext(selectedGoals);
|
|
127040
|
-
usedBudgetDrop = true;
|
|
127041
|
-
}
|
|
127042
|
-
let truncated = null;
|
|
127043
|
-
if (droppedGoalIds.length > 0) {
|
|
127044
|
-
truncated = {
|
|
127045
|
-
reason: usedBudgetDrop ? "budget" : "cap",
|
|
127046
|
-
requested: activeGoals.length,
|
|
127047
|
-
emitted: selectedGoals.length,
|
|
127048
|
-
droppedGoalIds,
|
|
127049
|
-
charBudget,
|
|
127050
|
-
producedChars: text.length
|
|
127051
|
-
};
|
|
127052
|
-
onTruncated?.(truncated);
|
|
127053
|
-
}
|
|
127054
|
-
return {
|
|
127055
|
-
text,
|
|
127056
|
-
emittedGoalIds: selectedGoals.map((goal) => goal.id),
|
|
127057
|
-
truncated
|
|
127058
|
-
};
|
|
127059
|
-
}
|
|
127060
|
-
var MAX_INJECTED_GOALS, resolvedActiveGoalLimit, DEFAULT_GOAL_INJECTION_CHAR_BUDGET;
|
|
127061
|
-
var init_goal_context_injector = __esm({
|
|
127062
|
-
"../engine/src/goal-context-injector.ts"() {
|
|
127063
|
-
"use strict";
|
|
127064
|
-
init_src();
|
|
127065
|
-
MAX_INJECTED_GOALS = 5;
|
|
127066
|
-
resolvedActiveGoalLimit = "ACTIVE_GOAL_LIMIT" in src_exports && typeof ACTIVE_GOAL_LIMIT === "number" ? ACTIVE_GOAL_LIMIT : MAX_INJECTED_GOALS;
|
|
127067
|
-
if (MAX_INJECTED_GOALS > resolvedActiveGoalLimit) {
|
|
127068
|
-
throw new Error(
|
|
127069
|
-
`MAX_INJECTED_GOALS (${MAX_INJECTED_GOALS}) cannot exceed ACTIVE_GOAL_LIMIT (${resolvedActiveGoalLimit})`
|
|
127070
|
-
);
|
|
127071
|
-
}
|
|
127072
|
-
DEFAULT_GOAL_INJECTION_CHAR_BUDGET = 600;
|
|
127073
|
-
}
|
|
127074
|
-
});
|
|
127075
|
-
|
|
127076
128114
|
// ../engine/src/experiment/benchmark-runner.ts
|
|
127077
128115
|
import { spawn as spawn7 } from "node:child_process";
|
|
127078
128116
|
import { mkdtemp, writeFile as writeFile17 } from "node:fs/promises";
|
|
@@ -132991,7 +134029,8 @@ var init_in_process_runtime = __esm({
|
|
|
132991
134029
|
this.usageLimitPauser = pauser;
|
|
132992
134030
|
}
|
|
132993
134031
|
/**
|
|
132994
|
-
* Set up event forwarding from TaskStore to runtime listeners
|
|
134032
|
+
* Set up event forwarding from TaskStore to runtime listeners
|
|
134033
|
+
* for task:created, task:moved, task:updated, and task:deleted.
|
|
132995
134034
|
*/
|
|
132996
134035
|
setupEventForwarding() {
|
|
132997
134036
|
this.taskStore.on("task:created", (task) => {
|
|
@@ -133006,6 +134045,10 @@ var init_in_process_runtime = __esm({
|
|
|
133006
134045
|
this.recordActivity();
|
|
133007
134046
|
this.emit("task:updated", task);
|
|
133008
134047
|
});
|
|
134048
|
+
this.taskStore.on("task:deleted", (task, meta) => {
|
|
134049
|
+
this.recordActivity();
|
|
134050
|
+
this.emit("task:deleted", task, meta);
|
|
134051
|
+
});
|
|
133009
134052
|
runtimeLog.log("Event forwarding setup complete");
|
|
133010
134053
|
}
|
|
133011
134054
|
/**
|
|
@@ -133056,7 +134099,7 @@ var init_in_process_runtime = __esm({
|
|
|
133056
134099
|
function generateCorrelationId() {
|
|
133057
134100
|
return `${Date.now()}-${Math.random().toString(36).slice(2, 11)}`;
|
|
133058
134101
|
}
|
|
133059
|
-
var START_RUNTIME, STOP_RUNTIME, GET_METRICS, OK, ERROR, PONG, TASK_CREATED, TASK_MOVED, TASK_UPDATED, ERROR_EVENT, HEALTH_CHANGED;
|
|
134102
|
+
var START_RUNTIME, STOP_RUNTIME, GET_METRICS, OK, ERROR, PONG, TASK_CREATED, TASK_MOVED, TASK_UPDATED, TASK_DELETED, ERROR_EVENT, HEALTH_CHANGED;
|
|
133060
134103
|
var init_ipc_protocol = __esm({
|
|
133061
134104
|
"../engine/src/ipc/ipc-protocol.ts"() {
|
|
133062
134105
|
"use strict";
|
|
@@ -133069,6 +134112,7 @@ var init_ipc_protocol = __esm({
|
|
|
133069
134112
|
TASK_CREATED = "TASK_CREATED";
|
|
133070
134113
|
TASK_MOVED = "TASK_MOVED";
|
|
133071
134114
|
TASK_UPDATED = "TASK_UPDATED";
|
|
134115
|
+
TASK_DELETED = "TASK_DELETED";
|
|
133072
134116
|
ERROR_EVENT = "ERROR_EVENT";
|
|
133073
134117
|
HEALTH_CHANGED = "HEALTH_CHANGED";
|
|
133074
134118
|
}
|
|
@@ -133453,6 +134497,9 @@ var init_child_process_runtime = __esm({
|
|
|
133453
134497
|
this.ipcHost.on(TASK_UPDATED, (payload) => {
|
|
133454
134498
|
this.emit("task:updated", payload.task);
|
|
133455
134499
|
});
|
|
134500
|
+
this.ipcHost.on(TASK_DELETED, (payload) => {
|
|
134501
|
+
this.emit("task:deleted", payload.task, payload.meta);
|
|
134502
|
+
});
|
|
133456
134503
|
this.ipcHost.on(ERROR_EVENT, (payload) => {
|
|
133457
134504
|
const error = new Error(payload.message);
|
|
133458
134505
|
if (payload.code) {
|
|
@@ -134575,6 +135622,9 @@ var init_project_manager = __esm({
|
|
|
134575
135622
|
runtime.on("task:updated", (task) => {
|
|
134576
135623
|
this.emit("task:updated", { projectId, projectName, task });
|
|
134577
135624
|
});
|
|
135625
|
+
runtime.on("task:deleted", (task, meta) => {
|
|
135626
|
+
this.emit("task:deleted", { projectId, projectName, task, meta });
|
|
135627
|
+
});
|
|
134578
135628
|
runtime.on("error", (error) => {
|
|
134579
135629
|
this.emit("error", { projectId, projectName, error });
|
|
134580
135630
|
this.logActivity(
|
|
@@ -136253,6 +137303,7 @@ var init_project_engine = __esm({
|
|
|
136253
137303
|
init_research_step_runner();
|
|
136254
137304
|
init_run_audit();
|
|
136255
137305
|
init_verification_followup_dedup();
|
|
137306
|
+
init_transient_error_detector();
|
|
136256
137307
|
init_tunnel_process_manager();
|
|
136257
137308
|
execFileAsync12 = promisify45(execFile17);
|
|
136258
137309
|
MERGE_HANDOFF_GRACE_MS = 300;
|
|
@@ -136323,6 +137374,10 @@ var init_project_engine = __esm({
|
|
|
136323
137374
|
manualMergeResolvers = /* @__PURE__ */ new Map();
|
|
136324
137375
|
shuttingDown = false;
|
|
136325
137376
|
static MAX_AUTO_MERGE_RETRIES = 3;
|
|
137377
|
+
/** FN-5697/FN-5674: cap transient provider/network abort retries in auto-merge.
|
|
137378
|
+
* Examples: "This operation was aborted", "socket hang up", `server_error`.
|
|
137379
|
+
* After this cap, the task is parked failed for human visibility. */
|
|
137380
|
+
static MAX_AUTO_MERGE_TRANSIENT_RETRIES = 3;
|
|
136326
137381
|
/** Cap on outer in-review→in-progress bounces caused by deterministic
|
|
136327
137382
|
* verification failures during auto-merge. After this many failed merges
|
|
136328
137383
|
* for the same task, we stop bouncing it back, mark it failed, and create
|
|
@@ -137305,7 +138360,15 @@ ${detail}`
|
|
|
137305
138360
|
this.internalEnqueueMerge(taskId);
|
|
137306
138361
|
continue;
|
|
137307
138362
|
}
|
|
137308
|
-
const blockerReason = getTaskHardMergeBlocker(
|
|
138363
|
+
const blockerReason = getTaskHardMergeBlocker({
|
|
138364
|
+
...task,
|
|
138365
|
+
// Merge-confirmed tasks have already landed. Treat stale merge
|
|
138366
|
+
// in-flight statuses as soft state to clear during finalization,
|
|
138367
|
+
// not hard blockers that park an otherwise confirmed merge as failed.
|
|
138368
|
+
paused: false,
|
|
138369
|
+
status: task.status === "merging" || task.status === "merging-pr" ? void 0 : task.status,
|
|
138370
|
+
error: void 0
|
|
138371
|
+
});
|
|
137309
138372
|
if (blockerReason) {
|
|
137310
138373
|
await store.updateTask(taskId, {
|
|
137311
138374
|
status: "failed",
|
|
@@ -137824,6 +138887,16 @@ ${detail}`
|
|
|
137824
138887
|
}
|
|
137825
138888
|
} else {
|
|
137826
138889
|
try {
|
|
138890
|
+
if (await this.maybeRetryTransientMerge(store, taskId, taskOnErr, errorMsg)) {
|
|
138891
|
+
continue;
|
|
138892
|
+
}
|
|
138893
|
+
if (this.isTransientMergeRetryExhausted(taskOnErr, errorMsg)) {
|
|
138894
|
+
await store.logEntry(
|
|
138895
|
+
taskId,
|
|
138896
|
+
`Auto-merge transient retries exhausted (${_ProjectEngine.MAX_AUTO_MERGE_TRANSIENT_RETRIES}/${_ProjectEngine.MAX_AUTO_MERGE_TRANSIENT_RETRIES}); parking task as failed: ${errorMsg}`,
|
|
138897
|
+
"MergeTransientRetryExhausted"
|
|
138898
|
+
);
|
|
138899
|
+
}
|
|
137827
138900
|
await store.updateTask(taskId, {
|
|
137828
138901
|
status: "failed",
|
|
137829
138902
|
mergeRetries: _ProjectEngine.MAX_AUTO_MERGE_RETRIES,
|
|
@@ -137842,6 +138915,16 @@ ${detail}`
|
|
|
137842
138915
|
}
|
|
137843
138916
|
} else {
|
|
137844
138917
|
try {
|
|
138918
|
+
if (await this.maybeRetryTransientMerge(store, taskId, taskOnErr, errorMsg)) {
|
|
138919
|
+
continue;
|
|
138920
|
+
}
|
|
138921
|
+
if (this.isTransientMergeRetryExhausted(taskOnErr, errorMsg)) {
|
|
138922
|
+
await store.logEntry(
|
|
138923
|
+
taskId,
|
|
138924
|
+
`Auto-merge transient retries exhausted (${_ProjectEngine.MAX_AUTO_MERGE_TRANSIENT_RETRIES}/${_ProjectEngine.MAX_AUTO_MERGE_TRANSIENT_RETRIES}); parking task as failed: ${errorMsg}`,
|
|
138925
|
+
"MergeTransientRetryExhausted"
|
|
138926
|
+
);
|
|
138927
|
+
}
|
|
137845
138928
|
await store.updateTask(taskId, {
|
|
137846
138929
|
status: "failed",
|
|
137847
138930
|
mergeRetries: _ProjectEngine.MAX_AUTO_MERGE_RETRIES,
|
|
@@ -137877,6 +138960,40 @@ ${detail}`
|
|
|
137877
138960
|
this.mergeRunning = false;
|
|
137878
138961
|
}
|
|
137879
138962
|
}
|
|
138963
|
+
isTransientMergeRetryExhausted(task, errorMsg) {
|
|
138964
|
+
if (!task || !isTransientError(errorMsg)) {
|
|
138965
|
+
return false;
|
|
138966
|
+
}
|
|
138967
|
+
const current = task.mergeTransientRetryCount ?? 0;
|
|
138968
|
+
return current >= _ProjectEngine.MAX_AUTO_MERGE_TRANSIENT_RETRIES;
|
|
138969
|
+
}
|
|
138970
|
+
async maybeRetryTransientMerge(store, taskId, taskOnErr, errorMsg) {
|
|
138971
|
+
if (!taskOnErr || !isTransientError(errorMsg)) {
|
|
138972
|
+
return false;
|
|
138973
|
+
}
|
|
138974
|
+
const currentRetries = taskOnErr.mergeTransientRetryCount ?? 0;
|
|
138975
|
+
if (currentRetries >= _ProjectEngine.MAX_AUTO_MERGE_TRANSIENT_RETRIES) {
|
|
138976
|
+
return false;
|
|
138977
|
+
}
|
|
138978
|
+
const nextRetryCount = currentRetries + 1;
|
|
138979
|
+
const delayMs = 5e3 * Math.pow(2, currentRetries);
|
|
138980
|
+
await store.updateTask(taskId, {
|
|
138981
|
+
mergeTransientRetryCount: nextRetryCount,
|
|
138982
|
+
status: null
|
|
138983
|
+
});
|
|
138984
|
+
await store.logEntry(
|
|
138985
|
+
taskId,
|
|
138986
|
+
`Auto-merge transient retry ${nextRetryCount}/${_ProjectEngine.MAX_AUTO_MERGE_TRANSIENT_RETRIES} scheduled in ${delayMs / 1e3}s: ${errorMsg}`,
|
|
138987
|
+
"MergeTransientRetry"
|
|
138988
|
+
);
|
|
138989
|
+
runtimeLog.log(
|
|
138990
|
+
`Auto-merge transient retry ${nextRetryCount}/${_ProjectEngine.MAX_AUTO_MERGE_TRANSIENT_RETRIES} for ${taskId} in ${delayMs / 1e3}s`
|
|
138991
|
+
);
|
|
138992
|
+
setTimeout(() => {
|
|
138993
|
+
if (!this.shuttingDown) this.internalEnqueueMerge(taskId);
|
|
138994
|
+
}, delayMs);
|
|
138995
|
+
return true;
|
|
138996
|
+
}
|
|
137880
138997
|
wireAutoMerge(store, _cwd) {
|
|
137881
138998
|
this.taskMovedHandler = async ({ task, to: to2 }) => {
|
|
137882
138999
|
if (to2 !== "in-review") return;
|
|
@@ -141441,6 +142558,9 @@ __export(src_exports2, {
|
|
|
141441
142558
|
ExperimentGitNotConfiguredError: () => ExperimentGitNotConfiguredError,
|
|
141442
142559
|
ExperimentMaxIterationsError: () => ExperimentMaxIterationsError,
|
|
141443
142560
|
ExperimentRevertConflictError: () => ExperimentRevertConflictError,
|
|
142561
|
+
GOAL_INJECTION_APPLIED: () => GOAL_INJECTION_APPLIED,
|
|
142562
|
+
GOAL_INJECTION_SKIPPED: () => GOAL_INJECTION_SKIPPED,
|
|
142563
|
+
GOAL_RETRIEVAL_INVOKED: () => GOAL_RETRIEVAL_INVOKED,
|
|
141444
142564
|
GitHubProvider: () => GitHubProvider,
|
|
141445
142565
|
HEARTBEAT_NO_TASK_SYSTEM_PROMPT: () => HEARTBEAT_NO_TASK_SYSTEM_PROMPT,
|
|
141446
142566
|
HEARTBEAT_PROCEDURE: () => HEARTBEAT_PROCEDURE,
|
|
@@ -141532,6 +142652,8 @@ __export(src_exports2, {
|
|
|
141532
142652
|
buildRuntimeResolutionContext: () => buildRuntimeResolutionContext,
|
|
141533
142653
|
buildSystemPromptWithInstructions: () => buildSystemPromptWithInstructions,
|
|
141534
142654
|
classifyBootstrapMisbinding: () => classifyBootstrapMisbinding,
|
|
142655
|
+
classifyGoalInjectionFailure: () => classifyGoalInjectionFailure,
|
|
142656
|
+
classifyGoalInjectionResult: () => classifyGoalInjectionResult,
|
|
141535
142657
|
classifyTaskError: () => classifyTaskError,
|
|
141536
142658
|
cleanupOrphanedWorktrees: () => cleanupOrphanedWorktrees,
|
|
141537
142659
|
cleanupSecretsEnvFile: () => cleanupSecretsEnvFile,
|
|
@@ -141556,6 +142678,9 @@ __export(src_exports2, {
|
|
|
141556
142678
|
describeModel: () => describeModel,
|
|
141557
142679
|
dropAutostashBySha: () => dropAutostashBySha,
|
|
141558
142680
|
dropAutostashHandle: () => dropAutostashHandle,
|
|
142681
|
+
emitGoalAnchoringAudit: () => emitGoalAnchoringAudit,
|
|
142682
|
+
emitGoalInjectionDiagnostic: () => emitGoalInjectionDiagnostic,
|
|
142683
|
+
emitGoalRetrievalAudit: () => emitGoalRetrievalAudit,
|
|
141559
142684
|
ensureDefaultHeartbeatProcedureFile: () => ensureDefaultHeartbeatProcedureFile,
|
|
141560
142685
|
executeApprovedAgentProvisioning: () => executeApprovedAgentProvisioning,
|
|
141561
142686
|
executeApprovedWorktrunkInstall: () => executeApprovedWorktrunkInstall,
|
|
@@ -141671,6 +142796,8 @@ var init_src2 = __esm({
|
|
|
141671
142796
|
init_web_fetch();
|
|
141672
142797
|
init_error_classifier();
|
|
141673
142798
|
init_goal_context_injector();
|
|
142799
|
+
init_goal_injection_diagnostics();
|
|
142800
|
+
init_goal_anchoring_audit();
|
|
141674
142801
|
init_worktrunk_installer();
|
|
141675
142802
|
init_worktrunk_failure_handler();
|
|
141676
142803
|
init_usage_limit_detector();
|
|
@@ -164297,12 +165424,17 @@ Status: ${createdMission.status}${createdMission.autoAdvance ? "\nAuto-advance:
|
|
|
164297
165424
|
)
|
|
164298
165425
|
}),
|
|
164299
165426
|
async execute(_toolCallId, params, _signal, _onUpdate, ctx) {
|
|
165427
|
+
const fnCtx = ctx;
|
|
164300
165428
|
const store = await getStore2(ctx.cwd);
|
|
164301
165429
|
const goalStore = store.getGoalStore();
|
|
164302
165430
|
const status = params.status ?? "active";
|
|
164303
165431
|
const goals = status === "all" ? goalStore.listGoals() : goalStore.listGoals({ status });
|
|
164304
165432
|
const activeCount = goalStore.listGoals({ status: "active" }).length;
|
|
164305
165433
|
const softWarning = activeCount >= 3;
|
|
165434
|
+
emitGoalRetrievalAudit(store, fnCtx, {
|
|
165435
|
+
toolName: "fn_goal_list",
|
|
165436
|
+
resultCount: goals.length
|
|
165437
|
+
});
|
|
164306
165438
|
const lines = [];
|
|
164307
165439
|
lines.push(`Goals (${goals.length}) [filter: ${status}]`);
|
|
164308
165440
|
lines.push(`Active: ${activeCount}/5`);
|
|
@@ -164421,10 +165553,17 @@ Status: ${goal.status}${softWarning ? `
|
|
|
164421
165553
|
id: Type8.String({ description: "Goal ID (G-\u2026)" })
|
|
164422
165554
|
}),
|
|
164423
165555
|
async execute(_toolCallId, params, _signal, _onUpdate, ctx) {
|
|
165556
|
+
const fnCtx = ctx;
|
|
164424
165557
|
const store = await getStore2(ctx.cwd);
|
|
164425
165558
|
const goalStore = store.getGoalStore();
|
|
164426
165559
|
const goal = goalStore.getGoal(params.id);
|
|
164427
165560
|
if (!goal) {
|
|
165561
|
+
emitGoalRetrievalAudit(store, fnCtx, {
|
|
165562
|
+
toolName: "fn_goal_show",
|
|
165563
|
+
resultCount: 0,
|
|
165564
|
+
goalId: params.id,
|
|
165565
|
+
notFound: true
|
|
165566
|
+
});
|
|
164428
165567
|
return {
|
|
164429
165568
|
isError: true,
|
|
164430
165569
|
content: [{ type: "text", text: `Goal ${params.id} not found` }],
|
|
@@ -164439,6 +165578,11 @@ Status: ${goal.status}${softWarning ? `
|
|
|
164439
165578
|
if (goal.description) {
|
|
164440
165579
|
lines.push(`Description: ${goal.description}`);
|
|
164441
165580
|
}
|
|
165581
|
+
emitGoalRetrievalAudit(store, fnCtx, {
|
|
165582
|
+
toolName: "fn_goal_show",
|
|
165583
|
+
resultCount: 1,
|
|
165584
|
+
goalId: params.id
|
|
165585
|
+
});
|
|
164442
165586
|
return {
|
|
164443
165587
|
content: [{ type: "text", text: lines.join("\n") }],
|
|
164444
165588
|
details: { goal }
|