@runfusion/fusion 0.14.2 → 0.14.3
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 +739 -67
- package/dist/client/assets/AgentDetailView-BBCnqhqI.js +18 -0
- package/dist/client/assets/{AgentsView-DkX0tzrN.js → AgentsView-BY-Yq-Te.js} +3 -3
- package/dist/client/assets/{ChatView-CEm2Hw6m.js → ChatView-DkoJNxFW.js} +1 -1
- package/dist/client/assets/{DevServerView-Bumvo_ge.js → DevServerView-qvs6pp6c.js} +1 -1
- package/dist/client/assets/{DirectoryPicker-CXN11cBp.js → DirectoryPicker-BkAIXNrP.js} +1 -1
- package/dist/client/assets/{DocumentsView-B71IqAxA.js → DocumentsView-BcaUGgaL.js} +1 -1
- package/dist/client/assets/{InsightsView-Bs4Rldu6.js → InsightsView-Dz9Ivclw.js} +1 -1
- package/dist/client/assets/{MemoryView-Bs7b_L2Q.js → MemoryView-BsweARBT.js} +1 -1
- package/dist/client/assets/{NodesView-BvAGTXbO.js → NodesView-bAU-v4bJ.js} +1 -1
- package/dist/client/assets/{PiExtensionsManager-3Kcc4uhA.js → PiExtensionsManager-C_U2g7y3.js} +1 -1
- package/dist/client/assets/{PluginManager-Ch-Xynlm.js → PluginManager-pIDsTk5v.js} +1 -1
- package/dist/client/assets/{ResearchView-Bj6Saqf6.js → ResearchView-D4Eib_uR.js} +1 -1
- package/dist/client/assets/{RoadmapsView-9qT8Vwd0.js → RoadmapsView-BaGwsUGS.js} +1 -1
- package/dist/client/assets/{SettingsModal-D4ERGQNQ.js → SettingsModal-BiZVi3cI.js} +1 -1
- package/dist/client/assets/SettingsModal-CRyg643t.js +31 -0
- package/dist/client/assets/{SetupWizardModal-Dv0rX2_o.js → SetupWizardModal-BcIGBBpA.js} +1 -1
- package/dist/client/assets/{SkillMultiselect-CSkXQzdv.js → SkillMultiselect-DPARHJeQ.js} +1 -1
- package/dist/client/assets/{SkillsView-2srXMOzj.js → SkillsView-Da_d_HPu.js} +1 -1
- package/dist/client/assets/{TodoView-CxPPIvw2.js → TodoView-5rAeqYtV.js} +1 -1
- package/dist/client/assets/{folder-open-FA1PwpXV.js → folder-open-CgjcFqww.js} +1 -1
- package/dist/client/assets/{index-CEavim6l.js → index-DoQ5ALYY.js} +25 -25
- package/dist/client/assets/{list-checks-6EktkUso.js → list-checks-C9YWtF7h.js} +1 -1
- package/dist/client/assets/{star-B6Th07jw.js → star-4nUh67-U.js} +1 -1
- package/dist/client/assets/{upload-BJwuErhV.js → upload-CEt5-Bnq.js} +1 -1
- package/dist/client/assets/{users-BrnPTF8H.js → users-4I0JDmgO.js} +1 -1
- package/dist/client/index.html +1 -1
- package/dist/client/version.json +1 -1
- package/dist/extension.js +518 -51
- package/dist/pi-claude-cli/index.ts +2 -2
- package/dist/pi-claude-cli/package.json +1 -1
- package/package.json +1 -1
- package/dist/client/assets/AgentDetailView-C2Iik3Qf.js +0 -18
- package/dist/client/assets/SettingsModal-Zo5qDGOq.js +0 -31
package/dist/extension.js
CHANGED
|
@@ -75,6 +75,7 @@ var init_settings_schema = __esm({
|
|
|
75
75
|
favoriteModels: void 0,
|
|
76
76
|
openrouterModelSync: true,
|
|
77
77
|
updateCheckEnabled: true,
|
|
78
|
+
fnBinaryCheckEnabled: true,
|
|
78
79
|
updateCheckFrequency: "daily",
|
|
79
80
|
showGitHubStarButton: true,
|
|
80
81
|
modelOnboardingComplete: void 0,
|
|
@@ -3290,6 +3291,12 @@ CREATE INDEX IF NOT EXISTS idxTodoItemsSortOrder ON todo_items(listId, sortOrder
|
|
|
3290
3291
|
if (!inMemory && !isAbsolute(fusionDir)) {
|
|
3291
3292
|
throw new Error(`[fusion] Database constructor requires an absolute fusionDir path, got: ${fusionDir}`);
|
|
3292
3293
|
}
|
|
3294
|
+
if (!inMemory && /\.fusion[\\/]\.fusion(?:[\\/]|$)/.test(fusionDir)) {
|
|
3295
|
+
throw new Error(
|
|
3296
|
+
`[fusion] Refusing to open Database at nested .fusion/.fusion path: ${fusionDir}
|
|
3297
|
+
This means a caller passed a .fusion directory where a project root was expected. Audit the call site for an extra \`join(rootDir, '.fusion')\` step.`
|
|
3298
|
+
);
|
|
3299
|
+
}
|
|
3293
3300
|
if (!inMemory && !existsSync(fusionDir)) {
|
|
3294
3301
|
mkdirSync(fusionDir, { recursive: true });
|
|
3295
3302
|
}
|
|
@@ -6016,6 +6023,7 @@ var init_agent_store = __esm({
|
|
|
6016
6023
|
};
|
|
6017
6024
|
const line = JSON.stringify(safeEntry) + "\n";
|
|
6018
6025
|
await appendFile(this.runLogPath(agentId, runId), line, "utf-8");
|
|
6026
|
+
this.emit("run:log", agentId, runId, safeEntry);
|
|
6019
6027
|
}
|
|
6020
6028
|
/**
|
|
6021
6029
|
* Read all log entries for a given run from its JSONL file.
|
|
@@ -36342,12 +36350,16 @@ var init_gh_cli = __esm({
|
|
|
36342
36350
|
|
|
36343
36351
|
// ../core/src/fn-binary.ts
|
|
36344
36352
|
import { spawn as spawn2 } from "node:child_process";
|
|
36345
|
-
import { platform as platform2 } from "node:os";
|
|
36353
|
+
import { platform as platform2, tmpdir as tmpdir2 } from "node:os";
|
|
36346
36354
|
function runProbe(command, args, timeoutMs) {
|
|
36347
36355
|
return new Promise((resolve19) => {
|
|
36348
36356
|
let stdout = "";
|
|
36349
36357
|
let stderr = "";
|
|
36350
|
-
const child = spawn2(command, args, {
|
|
36358
|
+
const child = spawn2(command, args, {
|
|
36359
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
36360
|
+
shell: false,
|
|
36361
|
+
cwd: tmpdir2()
|
|
36362
|
+
});
|
|
36351
36363
|
const timer = setTimeout(() => {
|
|
36352
36364
|
try {
|
|
36353
36365
|
child.kill("SIGKILL");
|
|
@@ -37665,7 +37677,7 @@ async function syncBackupAutomation(automationStore, settings) {
|
|
|
37665
37677
|
if (!AutomationStore2.isValidCron(schedule)) {
|
|
37666
37678
|
throw new Error(`Invalid backup schedule: ${schedule}`);
|
|
37667
37679
|
}
|
|
37668
|
-
const command = "
|
|
37680
|
+
const command = "fn backup --create";
|
|
37669
37681
|
if (existingSchedule) {
|
|
37670
37682
|
return await automationStore.updateSchedule(existingSchedule.id, {
|
|
37671
37683
|
scheduleType: "custom",
|
|
@@ -37698,7 +37710,7 @@ async function syncBackupRoutine(routineStore, settings) {
|
|
|
37698
37710
|
if (!RoutineStore2.isValidCron(schedule)) {
|
|
37699
37711
|
throw new Error(`Invalid backup schedule: ${schedule}`);
|
|
37700
37712
|
}
|
|
37701
|
-
const command = "
|
|
37713
|
+
const command = "fn backup --create";
|
|
37702
37714
|
const input = {
|
|
37703
37715
|
name: BACKUP_SCHEDULE_NAME,
|
|
37704
37716
|
description: "Automatic database backup based on project settings",
|
|
@@ -49573,7 +49585,7 @@ var require_dist3 = __commonJS({
|
|
|
49573
49585
|
|
|
49574
49586
|
// ../core/src/agent-companies-parser.ts
|
|
49575
49587
|
import { existsSync as existsSync17, mkdtempSync, readdirSync as readdirSync2, readFileSync as readFileSync4, rmSync, statSync as statSync4 } from "node:fs";
|
|
49576
|
-
import { tmpdir as
|
|
49588
|
+
import { tmpdir as tmpdir3 } from "node:os";
|
|
49577
49589
|
import { join as join20, resolve as resolve9 } from "node:path";
|
|
49578
49590
|
function slugifyAgentReference(value) {
|
|
49579
49591
|
return value.trim().toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/-+/g, "-").replace(/^-+|-+$/g, "");
|
|
@@ -49896,7 +49908,7 @@ async function extractTarArchive(archivePath, outputDir) {
|
|
|
49896
49908
|
}
|
|
49897
49909
|
async function parseCompanyArchive(archivePath) {
|
|
49898
49910
|
const resolvedArchivePath = resolve9(archivePath);
|
|
49899
|
-
const tempDir = mkdtempSync(join20(
|
|
49911
|
+
const tempDir = mkdtempSync(join20(tmpdir3(), "agent-companies-"));
|
|
49900
49912
|
try {
|
|
49901
49913
|
if (resolvedArchivePath.endsWith(".tar.gz") || resolvedArchivePath.endsWith(".tgz")) {
|
|
49902
49914
|
await extractTarArchive(resolvedArchivePath, tempDir);
|
|
@@ -65008,6 +65020,20 @@ The tool prevents your session from being killed by the inactivity watchdog duri
|
|
|
65008
65020
|
);
|
|
65009
65021
|
this.activeStepExecutors.delete(task.id);
|
|
65010
65022
|
}
|
|
65023
|
+
if (this.activeWorkflowStepSessions.has(task.id)) {
|
|
65024
|
+
executorLog.log(`${task.id} moved from in-progress to ${to} \u2014 terminating workflow step session`);
|
|
65025
|
+
this.pausedAborted.add(task.id);
|
|
65026
|
+
this.options.stuckTaskDetector?.untrackTask(task.id);
|
|
65027
|
+
const workflowSession = this.activeWorkflowStepSessions.get(task.id);
|
|
65028
|
+
const sessionWithAbort = workflowSession;
|
|
65029
|
+
if (typeof sessionWithAbort.abort === "function") {
|
|
65030
|
+
void sessionWithAbort.abort().catch((err) => {
|
|
65031
|
+
executorLog.warn(`Failed to abort workflow step session for ${task.id}: ${err}`);
|
|
65032
|
+
});
|
|
65033
|
+
}
|
|
65034
|
+
workflowSession.dispose();
|
|
65035
|
+
this.activeWorkflowStepSessions.delete(task.id);
|
|
65036
|
+
}
|
|
65011
65037
|
this.disposeSubagentsForTask(task.id, `parent moved from in-progress to ${to}`);
|
|
65012
65038
|
this.loopRecoveryState.delete(task.id);
|
|
65013
65039
|
this.spawnedAgents.delete(task.id);
|
|
@@ -65040,8 +65066,42 @@ The tool prevents your session from being killed by the inactivity watchdog duri
|
|
|
65040
65066
|
this.disposeSubagentsForTask(task.id, "task paused");
|
|
65041
65067
|
return;
|
|
65042
65068
|
}
|
|
65043
|
-
if (
|
|
65069
|
+
if (task.paused && this.activeWorkflowStepSessions.has(task.id)) {
|
|
65070
|
+
executorLog.log(`Pausing ${task.id} \u2014 terminating workflow step session`);
|
|
65071
|
+
this.pausedAborted.add(task.id);
|
|
65072
|
+
this.options.stuckTaskDetector?.untrackTask(task.id);
|
|
65073
|
+
const workflowSession = this.activeWorkflowStepSessions.get(task.id);
|
|
65074
|
+
const sessionWithAbort = workflowSession;
|
|
65075
|
+
if (typeof sessionWithAbort.abort === "function") {
|
|
65076
|
+
await sessionWithAbort.abort().catch(
|
|
65077
|
+
(err) => executorLog.warn(`Failed to abort workflow step session for pause ${task.id}: ${err}`)
|
|
65078
|
+
);
|
|
65079
|
+
}
|
|
65080
|
+
workflowSession.dispose();
|
|
65081
|
+
this.activeWorkflowStepSessions.delete(task.id);
|
|
65082
|
+
this.loopRecoveryState.delete(task.id);
|
|
65083
|
+
this.spawnedAgents.delete(task.id);
|
|
65084
|
+
this.stuckAborted.delete(task.id);
|
|
65085
|
+
this.disposeSubagentsForTask(task.id, "task paused");
|
|
65086
|
+
return;
|
|
65087
|
+
}
|
|
65088
|
+
if (!task.paused && task.column === "in-progress" && !this.activeSessions.has(task.id) && !this.activeStepExecutors.has(task.id) && !this.activeWorkflowStepSessions.has(task.id)) {
|
|
65044
65089
|
if (!this.executing.has(task.id) && !this.resumingUnpaused.has(task.id) && !this.recoveringCompleted.has(task.id)) {
|
|
65090
|
+
const pauseLabel = await this.getExecutionPauseLabel();
|
|
65091
|
+
if (pauseLabel) {
|
|
65092
|
+
executorLog.log(`Skipping unpause resume for ${task.id} \u2014 ${pauseLabel} active`);
|
|
65093
|
+
return;
|
|
65094
|
+
}
|
|
65095
|
+
if (this.isTaskWorkComplete(task) && !task.mergeDetails) {
|
|
65096
|
+
this.recoveringCompleted.add(task.id);
|
|
65097
|
+
executorLog.log(`${task.id} unpaused with completed work and no session \u2014 recovering directly to in-review`);
|
|
65098
|
+
void this.recoverCompletedTask(task).catch(
|
|
65099
|
+
(err) => executorLog.error(`Failed to recover completed unpaused task ${task.id}:`, err)
|
|
65100
|
+
).finally(() => {
|
|
65101
|
+
this.recoveringCompleted.delete(task.id);
|
|
65102
|
+
});
|
|
65103
|
+
return;
|
|
65104
|
+
}
|
|
65045
65105
|
this.resumingUnpaused.add(task.id);
|
|
65046
65106
|
executorLog.log(`Unpaused ${task.id} in-progress with no session \u2014 resuming execution`);
|
|
65047
65107
|
try {
|
|
@@ -65160,6 +65220,22 @@ The tool prevents your session from being killed by the inactivity watchdog duri
|
|
|
65160
65220
|
this.spawnedAgents.delete(taskId);
|
|
65161
65221
|
this.stuckAborted.delete(taskId);
|
|
65162
65222
|
}
|
|
65223
|
+
for (const [taskId, workflowSession] of this.activeWorkflowStepSessions) {
|
|
65224
|
+
executorLog.log(`Global pause \u2014 terminating workflow step session for ${taskId}`);
|
|
65225
|
+
this.pausedAborted.add(taskId);
|
|
65226
|
+
this.options.stuckTaskDetector?.untrackTask(taskId);
|
|
65227
|
+
const sessionWithAbort = workflowSession;
|
|
65228
|
+
if (typeof sessionWithAbort.abort === "function") {
|
|
65229
|
+
void sessionWithAbort.abort().catch((err) => {
|
|
65230
|
+
executorLog.warn(`Failed to abort workflow step session for ${taskId}: ${err}`);
|
|
65231
|
+
});
|
|
65232
|
+
}
|
|
65233
|
+
workflowSession.dispose();
|
|
65234
|
+
this.activeWorkflowStepSessions.delete(taskId);
|
|
65235
|
+
this.loopRecoveryState.delete(taskId);
|
|
65236
|
+
this.spawnedAgents.delete(taskId);
|
|
65237
|
+
this.stuckAborted.delete(taskId);
|
|
65238
|
+
}
|
|
65163
65239
|
}
|
|
65164
65240
|
});
|
|
65165
65241
|
}
|
|
@@ -65177,6 +65253,8 @@ The tool prevents your session from being killed by the inactivity watchdog duri
|
|
|
65177
65253
|
activeSessions = /* @__PURE__ */ new Map();
|
|
65178
65254
|
/** Active step-session executors per task (mutually exclusive with activeSessions). */
|
|
65179
65255
|
activeStepExecutors = /* @__PURE__ */ new Map();
|
|
65256
|
+
/** Active pre-merge workflow step sessions per task. */
|
|
65257
|
+
activeWorkflowStepSessions = /* @__PURE__ */ new Map();
|
|
65180
65258
|
/**
|
|
65181
65259
|
* Reviewer subagent sessions per task. Reviewers (`reviewer.ts`) create their
|
|
65182
65260
|
* own AgentSessions that aren't part of `activeSessions`/`activeStepExecutors`,
|
|
@@ -65223,6 +65301,69 @@ The tool prevents your session from being killed by the inactivity watchdog duri
|
|
|
65223
65301
|
await this.store.mergeTask(taskId);
|
|
65224
65302
|
return "merged";
|
|
65225
65303
|
}
|
|
65304
|
+
async getExecutionPauseLabel() {
|
|
65305
|
+
const settings = await this.store.getSettings();
|
|
65306
|
+
if (settings.globalPause) return "global pause";
|
|
65307
|
+
if (settings.enginePaused) return "engine pause";
|
|
65308
|
+
return null;
|
|
65309
|
+
}
|
|
65310
|
+
async shouldDeferCompletionForGlobalPause(taskId, context) {
|
|
65311
|
+
const settings = await this.store.getSettings();
|
|
65312
|
+
if (!settings.globalPause) {
|
|
65313
|
+
return false;
|
|
65314
|
+
}
|
|
65315
|
+
this.clearCompletedTaskWatchdog(taskId);
|
|
65316
|
+
executorLog.log(`${taskId}: completion handoff deferred \u2014 global pause active (${context})`);
|
|
65317
|
+
await this.store.logEntry(
|
|
65318
|
+
taskId,
|
|
65319
|
+
`Completion handoff deferred \u2014 global pause active (${context})`,
|
|
65320
|
+
void 0,
|
|
65321
|
+
this.currentRunContext
|
|
65322
|
+
).catch(() => void 0);
|
|
65323
|
+
return true;
|
|
65324
|
+
}
|
|
65325
|
+
async shouldDeferWorkflowStepCompletion(taskId, context) {
|
|
65326
|
+
let latestTask = null;
|
|
65327
|
+
try {
|
|
65328
|
+
latestTask = await this.store.getTask(taskId);
|
|
65329
|
+
} catch {
|
|
65330
|
+
latestTask = null;
|
|
65331
|
+
}
|
|
65332
|
+
if (latestTask?.paused || this.pausedAborted.has(taskId)) {
|
|
65333
|
+
this.clearCompletedTaskWatchdog(taskId);
|
|
65334
|
+
executorLog.log(`${taskId}: completion handoff deferred \u2014 task paused (${context})`);
|
|
65335
|
+
await this.store.logEntry(
|
|
65336
|
+
taskId,
|
|
65337
|
+
`Completion handoff deferred \u2014 task paused (${context})`,
|
|
65338
|
+
void 0,
|
|
65339
|
+
this.currentRunContext
|
|
65340
|
+
).catch(() => void 0);
|
|
65341
|
+
return true;
|
|
65342
|
+
}
|
|
65343
|
+
return this.shouldDeferCompletionForGlobalPause(taskId, context);
|
|
65344
|
+
}
|
|
65345
|
+
async parkTaskAfterWorkflowStepPause(taskId) {
|
|
65346
|
+
let latestTask = null;
|
|
65347
|
+
try {
|
|
65348
|
+
latestTask = await this.store.getTask(taskId);
|
|
65349
|
+
} catch {
|
|
65350
|
+
latestTask = null;
|
|
65351
|
+
}
|
|
65352
|
+
if (!latestTask?.paused) {
|
|
65353
|
+
return false;
|
|
65354
|
+
}
|
|
65355
|
+
executorLog.log(`${taskId}: workflow step interrupted by task pause \u2014 moving to todo`);
|
|
65356
|
+
await this.store.logEntry(
|
|
65357
|
+
taskId,
|
|
65358
|
+
"Execution paused during pre-merge workflow step \u2014 moved to todo",
|
|
65359
|
+
void 0,
|
|
65360
|
+
this.currentRunContext
|
|
65361
|
+
).catch(() => void 0);
|
|
65362
|
+
if (latestTask.column === "in-progress") {
|
|
65363
|
+
await this.store.moveTask(taskId, "todo", { preserveResumeState: true });
|
|
65364
|
+
}
|
|
65365
|
+
return true;
|
|
65366
|
+
}
|
|
65226
65367
|
/** Child agent sessions keyed by agent ID. Used for termination. */
|
|
65227
65368
|
childSessions = /* @__PURE__ */ new Map();
|
|
65228
65369
|
/** Total count of currently spawned agents (across all parents). */
|
|
@@ -65417,11 +65558,15 @@ The tool prevents your session from being killed by the inactivity watchdog duri
|
|
|
65417
65558
|
this.clearCompletedTaskWatchdog(taskId);
|
|
65418
65559
|
const handle = setTimeout(async () => {
|
|
65419
65560
|
this.completedTaskWatchdogs.delete(taskId);
|
|
65420
|
-
if (this.recoveringCompleted.has(taskId) || this.executing.has(taskId) || this.activeSessions.has(taskId) || this.activeStepExecutors.has(taskId) || this.resumingUnpaused.has(taskId)) {
|
|
65561
|
+
if (this.recoveringCompleted.has(taskId) || this.executing.has(taskId) || this.activeSessions.has(taskId) || this.activeStepExecutors.has(taskId) || this.activeWorkflowStepSessions.has(taskId) || this.resumingUnpaused.has(taskId)) {
|
|
65421
65562
|
return;
|
|
65422
65563
|
}
|
|
65423
65564
|
this.recoveringCompleted.add(taskId);
|
|
65424
65565
|
try {
|
|
65566
|
+
const pauseLabel = await this.getExecutionPauseLabel();
|
|
65567
|
+
if (pauseLabel) {
|
|
65568
|
+
return;
|
|
65569
|
+
}
|
|
65425
65570
|
let currentTask = null;
|
|
65426
65571
|
try {
|
|
65427
65572
|
currentTask = await this.store.getTask(taskId);
|
|
@@ -65467,6 +65612,11 @@ The tool prevents your session from being killed by the inactivity watchdog duri
|
|
|
65467
65612
|
* stuck.
|
|
65468
65613
|
*/
|
|
65469
65614
|
async performWorkflowRerunBounce(taskId, worktreePath, preserveResumeState = true) {
|
|
65615
|
+
const pauseLabel = await this.getExecutionPauseLabel();
|
|
65616
|
+
if (pauseLabel) {
|
|
65617
|
+
executorLog.log(`${taskId}: workflow rerun deferred \u2014 ${pauseLabel} active`);
|
|
65618
|
+
return "deferred-paused";
|
|
65619
|
+
}
|
|
65470
65620
|
if (this.workflowRerunPending.has(taskId)) {
|
|
65471
65621
|
executorLog.warn(`${taskId}: workflow rerun bounce already in flight \u2014 skipping re-entry`);
|
|
65472
65622
|
return "skipped-pending";
|
|
@@ -65477,6 +65627,10 @@ The tool prevents your session from being killed by the inactivity watchdog duri
|
|
|
65477
65627
|
if (!latestTask) {
|
|
65478
65628
|
throw new Error("task missing during workflow rerun bounce");
|
|
65479
65629
|
}
|
|
65630
|
+
if (latestTask.paused) {
|
|
65631
|
+
executorLog.log(`${taskId}: workflow rerun deferred \u2014 task is paused`);
|
|
65632
|
+
return "deferred-paused";
|
|
65633
|
+
}
|
|
65480
65634
|
if (latestTask.column === "in-progress") {
|
|
65481
65635
|
const originalExecutionStartedAt = latestTask.executionStartedAt;
|
|
65482
65636
|
if (preserveResumeState) {
|
|
@@ -65488,11 +65642,21 @@ The tool prevents your session from being killed by the inactivity watchdog duri
|
|
|
65488
65642
|
worktree: worktreePath,
|
|
65489
65643
|
executionStartedAt: originalExecutionStartedAt ?? null
|
|
65490
65644
|
});
|
|
65645
|
+
const pauseLabelAfterTodo = await this.getExecutionPauseLabel();
|
|
65646
|
+
if (pauseLabelAfterTodo) {
|
|
65647
|
+
executorLog.log(`${taskId}: workflow rerun parked in todo \u2014 ${pauseLabelAfterTodo} became active during bounce`);
|
|
65648
|
+
return "deferred-paused";
|
|
65649
|
+
}
|
|
65491
65650
|
await this.store.moveTask(taskId, "in-progress");
|
|
65492
65651
|
return "bounced";
|
|
65493
65652
|
}
|
|
65494
65653
|
if (latestTask.column === "todo") {
|
|
65495
65654
|
await this.store.updateTask(taskId, { worktree: worktreePath });
|
|
65655
|
+
const pauseLabelBeforeResume = await this.getExecutionPauseLabel();
|
|
65656
|
+
if (pauseLabelBeforeResume) {
|
|
65657
|
+
executorLog.log(`${taskId}: workflow rerun parked in todo \u2014 ${pauseLabelBeforeResume} became active before resume`);
|
|
65658
|
+
return "deferred-paused";
|
|
65659
|
+
}
|
|
65496
65660
|
await this.store.moveTask(taskId, "in-progress");
|
|
65497
65661
|
return "bounced";
|
|
65498
65662
|
}
|
|
@@ -65508,8 +65672,10 @@ The tool prevents your session from being killed by the inactivity watchdog duri
|
|
|
65508
65672
|
const outcome = await this.performWorkflowRerunBounce(taskId, worktreePath, preserveResumeState);
|
|
65509
65673
|
if (outcome === "bounced") {
|
|
65510
65674
|
executorLog.log(successMessage);
|
|
65511
|
-
} else {
|
|
65675
|
+
} else if (outcome === "skipped-pending") {
|
|
65512
65676
|
executorLog.warn(`${taskId}: rerun bounce skipped \u2014 another bounce already in flight`);
|
|
65677
|
+
} else {
|
|
65678
|
+
executorLog.log(`${taskId}: rerun bounce deferred while pause is active`);
|
|
65513
65679
|
}
|
|
65514
65680
|
} catch (err) {
|
|
65515
65681
|
const errorMessage = err instanceof Error ? err.message : String(err);
|
|
@@ -65518,6 +65684,11 @@ The tool prevents your session from being killed by the inactivity watchdog duri
|
|
|
65518
65684
|
}, 0);
|
|
65519
65685
|
const watchdog = setTimeout(async () => {
|
|
65520
65686
|
this.workflowRerunWatchdogs.delete(taskId);
|
|
65687
|
+
const pauseLabel = await this.getExecutionPauseLabel();
|
|
65688
|
+
if (pauseLabel) {
|
|
65689
|
+
executorLog.log(`${taskId}: workflow rerun watchdog skipped \u2014 ${pauseLabel} active`);
|
|
65690
|
+
return;
|
|
65691
|
+
}
|
|
65521
65692
|
let currentTask = null;
|
|
65522
65693
|
try {
|
|
65523
65694
|
currentTask = await this.store.getTask(taskId);
|
|
@@ -65540,7 +65711,7 @@ The tool prevents your session from being killed by the inactivity watchdog duri
|
|
|
65540
65711
|
const outcome = await this.performWorkflowRerunBounce(taskId, worktreePath, preserveResumeState);
|
|
65541
65712
|
if (outcome === "bounced") {
|
|
65542
65713
|
executorLog.warn(`${taskId}: workflow rerun watchdog retry succeeded`);
|
|
65543
|
-
} else {
|
|
65714
|
+
} else if (outcome === "skipped-pending") {
|
|
65544
65715
|
executorLog.error(
|
|
65545
65716
|
`${taskId}: workflow rerun watchdog retry skipped \u2014 original bounce still in flight after ${WORKFLOW_RERUN_WATCHDOG_MS / 1e3}s; task may be stuck`
|
|
65546
65717
|
);
|
|
@@ -65548,6 +65719,8 @@ The tool prevents your session from being killed by the inactivity watchdog duri
|
|
|
65548
65719
|
taskId,
|
|
65549
65720
|
`Workflow rerun watchdog retry skipped \u2014 original bounce still in flight after ${WORKFLOW_RERUN_WATCHDOG_MS / 1e3}s; task may be stuck`
|
|
65550
65721
|
).catch(() => void 0);
|
|
65722
|
+
} else {
|
|
65723
|
+
executorLog.log(`${taskId}: workflow rerun watchdog retry deferred while pause is active`);
|
|
65551
65724
|
}
|
|
65552
65725
|
} catch (err) {
|
|
65553
65726
|
const errorMessage = err instanceof Error ? err.message : String(err);
|
|
@@ -65670,11 +65843,17 @@ The tool prevents your session from being killed by the inactivity watchdog duri
|
|
|
65670
65843
|
*/
|
|
65671
65844
|
async recoverCompletedTask(task) {
|
|
65672
65845
|
try {
|
|
65673
|
-
if (this.executing.has(task.id) || this.activeSessions.has(task.id) || this.activeStepExecutors.has(task.id) || this.resumingUnpaused.has(task.id)) {
|
|
65846
|
+
if (this.executing.has(task.id) || this.activeSessions.has(task.id) || this.activeStepExecutors.has(task.id) || this.activeWorkflowStepSessions.has(task.id) || this.resumingUnpaused.has(task.id)) {
|
|
65674
65847
|
executorLog.log(`${task.id}: skipping recoverCompletedTask \u2014 task has active execution in flight`);
|
|
65675
65848
|
return false;
|
|
65676
65849
|
}
|
|
65677
65850
|
const settings = await this.store.getSettings();
|
|
65851
|
+
if (settings.globalPause || settings.enginePaused) {
|
|
65852
|
+
executorLog.log(
|
|
65853
|
+
`${task.id}: skipping recoverCompletedTask \u2014 ${settings.globalPause ? "global pause" : "engine pause"} active`
|
|
65854
|
+
);
|
|
65855
|
+
return false;
|
|
65856
|
+
}
|
|
65678
65857
|
if (task.worktree && existsSync27(task.worktree)) {
|
|
65679
65858
|
const modifiedFiles = await this.captureModifiedFiles(task.worktree, task.baseCommitSha);
|
|
65680
65859
|
if (modifiedFiles.length > 0) {
|
|
@@ -65682,7 +65861,16 @@ The tool prevents your session from being killed by the inactivity watchdog duri
|
|
|
65682
65861
|
executorLog.log(`${task.id}: recovered ${modifiedFiles.length} modified files`);
|
|
65683
65862
|
}
|
|
65684
65863
|
if (task.executionMode !== "fast") {
|
|
65864
|
+
if (await this.shouldDeferCompletionForGlobalPause(task.id, "before workflow steps during completed-task recovery")) {
|
|
65865
|
+
return false;
|
|
65866
|
+
}
|
|
65685
65867
|
const workflowResult = await this.runWorkflowSteps(task, task.worktree, settings);
|
|
65868
|
+
if (workflowResult === "deferred-paused") {
|
|
65869
|
+
if (this.pausedAborted.has(task.id)) {
|
|
65870
|
+
this.pausedAborted.delete(task.id);
|
|
65871
|
+
}
|
|
65872
|
+
return false;
|
|
65873
|
+
}
|
|
65686
65874
|
if (!workflowResult.allPassed) {
|
|
65687
65875
|
await this.sendTaskBackForFix(task, task.worktree, workflowResult.feedback, workflowResult.stepName || "Unknown", "Workflow step failed during recovery", false);
|
|
65688
65876
|
return true;
|
|
@@ -65691,6 +65879,9 @@ The tool prevents your session from being killed by the inactivity watchdog duri
|
|
|
65691
65879
|
executorLog.log(`${task.id}: fast mode \u2014 skipping workflow steps on auto-recovery`);
|
|
65692
65880
|
}
|
|
65693
65881
|
}
|
|
65882
|
+
if (await this.shouldDeferCompletionForGlobalPause(task.id, "before in-review transition during completed-task recovery")) {
|
|
65883
|
+
return false;
|
|
65884
|
+
}
|
|
65694
65885
|
await this.persistTokenUsage(task.id);
|
|
65695
65886
|
await this.store.moveTask(task.id, "in-review");
|
|
65696
65887
|
this.clearCompletedTaskWatchdog(task.id);
|
|
@@ -65756,6 +65947,13 @@ The tool prevents your session from being killed by the inactivity watchdog duri
|
|
|
65756
65947
|
* directly to in-review without spawning a new agent session.
|
|
65757
65948
|
*/
|
|
65758
65949
|
async resumeOrphaned() {
|
|
65950
|
+
const settings = await this.store.getSettings();
|
|
65951
|
+
if (settings.globalPause || settings.enginePaused) {
|
|
65952
|
+
executorLog.log(
|
|
65953
|
+
`resumeOrphaned skipped \u2014 ${settings.globalPause ? "global pause" : "engine pause"} is active`
|
|
65954
|
+
);
|
|
65955
|
+
return;
|
|
65956
|
+
}
|
|
65759
65957
|
const tasks = await this.store.listTasks({ slim: true, column: "in-progress" });
|
|
65760
65958
|
const inProgress = tasks.filter(
|
|
65761
65959
|
(t) => t.column === "in-progress" && !this.executing.has(t.id) && !t.paused
|
|
@@ -66194,8 +66392,21 @@ The tool prevents your session from being killed by the inactivity watchdog duri
|
|
|
66194
66392
|
await audit.filesystem({ type: "file:capture-modified", target: task.id, metadata: { files: modifiedFiles } });
|
|
66195
66393
|
}
|
|
66196
66394
|
this.scheduleCompletedTaskWatchdog(task.id, "step-session completion");
|
|
66395
|
+
if (await this.shouldDeferCompletionForGlobalPause(task.id, "before workflow steps after step-session completion")) {
|
|
66396
|
+
return;
|
|
66397
|
+
}
|
|
66197
66398
|
if (executionMode !== "fast") {
|
|
66198
66399
|
const workflowResult = await this.runWorkflowSteps(task, worktreePath, settings);
|
|
66400
|
+
if (workflowResult === "deferred-paused") {
|
|
66401
|
+
if (await this.parkTaskAfterWorkflowStepPause(task.id)) {
|
|
66402
|
+
this.pausedAborted.delete(task.id);
|
|
66403
|
+
return;
|
|
66404
|
+
}
|
|
66405
|
+
if (this.pausedAborted.has(task.id)) {
|
|
66406
|
+
this.pausedAborted.delete(task.id);
|
|
66407
|
+
}
|
|
66408
|
+
return;
|
|
66409
|
+
}
|
|
66199
66410
|
if (!workflowResult.allPassed) {
|
|
66200
66411
|
if (workflowResult.revisionRequested) {
|
|
66201
66412
|
await this.handleWorkflowRevisionRequest(task, worktreePath, workflowResult.feedback, workflowResult.stepName);
|
|
@@ -66213,6 +66424,9 @@ The tool prevents your session from being killed by the inactivity watchdog duri
|
|
|
66213
66424
|
await this.store.logEntry(task.id, "Fast mode \u2014 pre-merge workflow steps skipped", void 0, this.currentRunContext);
|
|
66214
66425
|
}
|
|
66215
66426
|
await this.store.updateTask(task.id, { workflowStepRetries: void 0, taskDoneRetryCount: null });
|
|
66427
|
+
if (await this.shouldDeferCompletionForGlobalPause(task.id, "before in-review transition after step-session completion")) {
|
|
66428
|
+
return;
|
|
66429
|
+
}
|
|
66216
66430
|
await this.store.moveTask(task.id, "in-review");
|
|
66217
66431
|
this.clearCompletedTaskWatchdog(task.id);
|
|
66218
66432
|
await audit.database({ type: "task:move", target: task.id, metadata: { to: "in-review" } });
|
|
@@ -66565,6 +66779,9 @@ The tool prevents your session from being killed by the inactivity watchdog duri
|
|
|
66565
66779
|
this.pausedAborted.delete(task.id);
|
|
66566
66780
|
wasPaused = true;
|
|
66567
66781
|
if (await this.shouldFinalizeCompletedTask(task.id, taskDone)) {
|
|
66782
|
+
if (await this.shouldDeferCompletionForGlobalPause(task.id, "paused after completion")) {
|
|
66783
|
+
return;
|
|
66784
|
+
}
|
|
66568
66785
|
executorLog.log(`${task.id} paused after completion (graceful session exit) \u2014 finalizing to in-review`);
|
|
66569
66786
|
await this.store.logEntry(task.id, "Execution paused after completion \u2014 finalizing to in-review");
|
|
66570
66787
|
await this.persistTokenUsage(task.id);
|
|
@@ -66601,8 +66818,23 @@ The tool prevents your session from being killed by the inactivity watchdog duri
|
|
|
66601
66818
|
executorLog.log(`${task.id}: captured ${modifiedFiles.length} modified files`);
|
|
66602
66819
|
}
|
|
66603
66820
|
this.scheduleCompletedTaskWatchdog(task.id, "task completion");
|
|
66821
|
+
if (await this.shouldDeferCompletionForGlobalPause(task.id, "before workflow steps after task completion")) {
|
|
66822
|
+
return;
|
|
66823
|
+
}
|
|
66604
66824
|
if (executionMode !== "fast") {
|
|
66605
66825
|
const workflowResult = await this.runWorkflowSteps(task, worktreePath, settings);
|
|
66826
|
+
if (workflowResult === "deferred-paused") {
|
|
66827
|
+
if (await this.parkTaskAfterWorkflowStepPause(task.id)) {
|
|
66828
|
+
this.pausedAborted.delete(task.id);
|
|
66829
|
+
wasPaused = true;
|
|
66830
|
+
return;
|
|
66831
|
+
}
|
|
66832
|
+
if (this.pausedAborted.has(task.id)) {
|
|
66833
|
+
this.pausedAborted.delete(task.id);
|
|
66834
|
+
wasPaused = true;
|
|
66835
|
+
}
|
|
66836
|
+
return;
|
|
66837
|
+
}
|
|
66606
66838
|
if (!workflowResult.allPassed) {
|
|
66607
66839
|
if (workflowResult.revisionRequested) {
|
|
66608
66840
|
await this.handleWorkflowRevisionRequest(task, worktreePath, workflowResult.feedback, workflowResult.stepName);
|
|
@@ -66620,6 +66852,9 @@ The tool prevents your session from being killed by the inactivity watchdog duri
|
|
|
66620
66852
|
await this.store.logEntry(task.id, "Fast mode \u2014 pre-merge workflow steps skipped", void 0, this.currentRunContext);
|
|
66621
66853
|
}
|
|
66622
66854
|
await this.store.updateTask(task.id, { workflowStepRetries: void 0, taskDoneRetryCount: null });
|
|
66855
|
+
if (await this.shouldDeferCompletionForGlobalPause(task.id, "before in-review transition after task completion")) {
|
|
66856
|
+
return;
|
|
66857
|
+
}
|
|
66623
66858
|
await this.persistTokenUsage(task.id);
|
|
66624
66859
|
await this.store.moveTask(task.id, "in-review");
|
|
66625
66860
|
this.clearCompletedTaskWatchdog(task.id);
|
|
@@ -66744,8 +66979,23 @@ The tool prevents your session from being killed by the inactivity watchdog duri
|
|
|
66744
66979
|
executorLog.log(`${task.id}: captured ${modifiedFiles.length} modified files`);
|
|
66745
66980
|
}
|
|
66746
66981
|
this.scheduleCompletedTaskWatchdog(task.id, "task completion retry");
|
|
66982
|
+
if (await this.shouldDeferCompletionForGlobalPause(task.id, "before workflow steps after task completion retry")) {
|
|
66983
|
+
return;
|
|
66984
|
+
}
|
|
66747
66985
|
if (executionMode !== "fast") {
|
|
66748
66986
|
const workflowResult = await this.runWorkflowSteps(task, worktreePath, settings);
|
|
66987
|
+
if (workflowResult === "deferred-paused") {
|
|
66988
|
+
if (await this.parkTaskAfterWorkflowStepPause(task.id)) {
|
|
66989
|
+
this.pausedAborted.delete(task.id);
|
|
66990
|
+
wasPaused = true;
|
|
66991
|
+
return;
|
|
66992
|
+
}
|
|
66993
|
+
if (this.pausedAborted.has(task.id)) {
|
|
66994
|
+
this.pausedAborted.delete(task.id);
|
|
66995
|
+
wasPaused = true;
|
|
66996
|
+
}
|
|
66997
|
+
return;
|
|
66998
|
+
}
|
|
66749
66999
|
if (!workflowResult.allPassed) {
|
|
66750
67000
|
if (workflowResult.revisionRequested) {
|
|
66751
67001
|
await this.handleWorkflowRevisionRequest(task, worktreePath, workflowResult.feedback, workflowResult.stepName);
|
|
@@ -66759,6 +67009,9 @@ The tool prevents your session from being killed by the inactivity watchdog duri
|
|
|
66759
67009
|
await this.store.logEntry(task.id, "Fast mode \u2014 pre-merge workflow steps skipped", void 0, this.currentRunContext);
|
|
66760
67010
|
}
|
|
66761
67011
|
await this.store.updateTask(task.id, { workflowStepRetries: void 0, taskDoneRetryCount: null });
|
|
67012
|
+
if (await this.shouldDeferCompletionForGlobalPause(task.id, "before in-review transition after task completion retry")) {
|
|
67013
|
+
return;
|
|
67014
|
+
}
|
|
66762
67015
|
await this.persistTokenUsage(task.id);
|
|
66763
67016
|
await this.store.moveTask(task.id, "in-review");
|
|
66764
67017
|
this.clearCompletedTaskWatchdog(task.id);
|
|
@@ -66851,6 +67104,9 @@ The tool prevents your session from being killed by the inactivity watchdog duri
|
|
|
66851
67104
|
} else if (this.pausedAborted.has(task.id)) {
|
|
66852
67105
|
this.pausedAborted.delete(task.id);
|
|
66853
67106
|
if (await this.shouldFinalizeCompletedTask(task.id, taskDone)) {
|
|
67107
|
+
if (await this.shouldDeferCompletionForGlobalPause(task.id, "paused after completion")) {
|
|
67108
|
+
return;
|
|
67109
|
+
}
|
|
66854
67110
|
executorLog.log(`${task.id} paused after completion \u2014 finalizing to in-review`);
|
|
66855
67111
|
await this.store.logEntry(task.id, "Execution paused after completion \u2014 finalizing to in-review", void 0, this.currentRunContext);
|
|
66856
67112
|
await this.persistTokenUsage(task.id);
|
|
@@ -67209,22 +67465,28 @@ The tool prevents your session from being killed by the inactivity watchdog duri
|
|
|
67209
67465
|
if (params.summary) {
|
|
67210
67466
|
await store.updateTask(taskId, { summary: params.summary });
|
|
67211
67467
|
}
|
|
67212
|
-
await store.
|
|
67468
|
+
const settings = await store.getSettings();
|
|
67469
|
+
const hardPauseActive = Boolean(task.paused || settings.globalPause);
|
|
67470
|
+
if (hardPauseActive) {
|
|
67471
|
+
await store.updateTask(taskId, { status: null });
|
|
67472
|
+
} else {
|
|
67473
|
+
await store.updateTask(taskId, { paused: false, status: null });
|
|
67474
|
+
}
|
|
67213
67475
|
await store.logEntry(taskId, "Task marked done by agent");
|
|
67214
67476
|
const latestTask = await store.getTask(taskId);
|
|
67215
67477
|
let latestColumn = latestTask.column;
|
|
67216
67478
|
if (latestColumn === "todo") {
|
|
67217
67479
|
await store.logEntry(
|
|
67218
67480
|
taskId,
|
|
67219
|
-
"fn_task_done called while task was in todo \u2014 promoting to in-progress before completion handoff"
|
|
67481
|
+
hardPauseActive ? "fn_task_done called while task was in todo during pause \u2014 promoting to in-progress for deferred completion handoff" : "fn_task_done called while task was in todo \u2014 promoting to in-progress before completion handoff"
|
|
67220
67482
|
);
|
|
67221
67483
|
await store.moveTask(taskId, "in-progress");
|
|
67222
67484
|
latestColumn = "in-progress";
|
|
67223
67485
|
}
|
|
67224
|
-
if (latestColumn === "in-progress") {
|
|
67486
|
+
if (latestColumn === "in-progress" && !hardPauseActive) {
|
|
67225
67487
|
this.scheduleCompletedTaskWatchdog(taskId, "fn_task_done");
|
|
67226
67488
|
}
|
|
67227
|
-
const successMessage = params.summary ? "Task marked complete with summary. All steps done. Moving to in-review." : "Task marked complete. All steps done. Moving to in-review.";
|
|
67489
|
+
const successMessage = hardPauseActive ? "Task marked complete. Completion handoff deferred until pause is cleared." : params.summary ? "Task marked complete with summary. All steps done. Moving to in-review." : "Task marked complete. All steps done. Moving to in-review.";
|
|
67228
67490
|
return {
|
|
67229
67491
|
content: [{ type: "text", text: successMessage }],
|
|
67230
67492
|
details: {}
|
|
@@ -67799,6 +68061,9 @@ ${failureFeedback}
|
|
|
67799
68061
|
await this.store.updateTask(task.id, { workflowStepResults: results });
|
|
67800
68062
|
continue;
|
|
67801
68063
|
}
|
|
68064
|
+
if (await this.shouldDeferWorkflowStepCompletion(task.id, `before workflow step '${ws.name}'`)) {
|
|
68065
|
+
return "deferred-paused";
|
|
68066
|
+
}
|
|
67802
68067
|
await this.store.logEntry(task.id, `[pre-merge] Starting workflow step: ${ws.name} (${stepMode} mode)`);
|
|
67803
68068
|
executorLog.log(`${task.id} \u2014 [pre-merge] running workflow step: ${ws.name} (${stepMode} mode)`);
|
|
67804
68069
|
const startedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -67813,6 +68078,9 @@ ${failureFeedback}
|
|
|
67813
68078
|
await this.store.updateTask(task.id, { workflowStepResults: results });
|
|
67814
68079
|
try {
|
|
67815
68080
|
const result = stepMode === "script" ? await this.executeScriptWorkflowStep(task, ws, worktreePath, settings) : await this.executeWorkflowStep(task, ws, worktreePath, settings);
|
|
68081
|
+
if (await this.shouldDeferWorkflowStepCompletion(task.id, `workflow step '${ws.name}'`)) {
|
|
68082
|
+
return "deferred-paused";
|
|
68083
|
+
}
|
|
67816
68084
|
const completedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
67817
68085
|
if (result.success) {
|
|
67818
68086
|
await this.store.logEntry(task.id, `[timing] Workflow step '${ws.name}' completed in ${Date.now() - stepStartedAtMs}ms`);
|
|
@@ -67878,6 +68146,9 @@ ${failureFeedback}
|
|
|
67878
68146
|
};
|
|
67879
68147
|
}
|
|
67880
68148
|
} catch (err) {
|
|
68149
|
+
if (await this.shouldDeferWorkflowStepCompletion(task.id, `workflow step '${ws.name}'`)) {
|
|
68150
|
+
return "deferred-paused";
|
|
68151
|
+
}
|
|
67881
68152
|
const { message: errorMessage, detail: errorDetail, stack: errorStack } = formatError(err);
|
|
67882
68153
|
const completedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
67883
68154
|
await this.store.logEntry(
|
|
@@ -68043,6 +68314,7 @@ and show an appropriate message to the user.\`
|
|
|
68043
68314
|
task.id,
|
|
68044
68315
|
`Workflow step '${workflowStep.name}' using model: ${describeModel(session)}${useOverride && attemptLabel === "primary" ? " (workflow step override)" : ""}${attemptLabel === "fallback" ? " (fallback after timeout)" : ""}`
|
|
68045
68316
|
);
|
|
68317
|
+
this.activeWorkflowStepSessions.set(task.id, session);
|
|
68046
68318
|
let output = "";
|
|
68047
68319
|
session.subscribe((event) => {
|
|
68048
68320
|
if (event.type === "message_update") {
|
|
@@ -68115,6 +68387,10 @@ Review the work done in this worktree and evaluate it against the criteria in yo
|
|
|
68115
68387
|
return { success: false, error: errorMessage };
|
|
68116
68388
|
} finally {
|
|
68117
68389
|
if (timeoutHandle) clearTimeout(timeoutHandle);
|
|
68390
|
+
const activeWorkflowStepSession = this.activeWorkflowStepSessions.get(task.id);
|
|
68391
|
+
if (activeWorkflowStepSession === session) {
|
|
68392
|
+
this.activeWorkflowStepSessions.delete(task.id);
|
|
68393
|
+
}
|
|
68118
68394
|
void timedOut;
|
|
68119
68395
|
}
|
|
68120
68396
|
};
|
|
@@ -69858,6 +70134,15 @@ var init_scheduler = __esm({
|
|
|
69858
70134
|
schedulerLog.log(`Task ${task.id} is paused \u2014 skipping dispatch`);
|
|
69859
70135
|
continue;
|
|
69860
70136
|
}
|
|
70137
|
+
const latestSettings = await this.store.getSettings();
|
|
70138
|
+
if (latestSettings.globalPause) {
|
|
70139
|
+
schedulerLog.log(`Task ${task.id} dispatch aborted \u2014 globalPause became active mid-pass`);
|
|
70140
|
+
continue;
|
|
70141
|
+
}
|
|
70142
|
+
if (latestSettings.enginePaused) {
|
|
70143
|
+
schedulerLog.log(`Task ${task.id} dispatch aborted \u2014 enginePaused became active mid-pass`);
|
|
70144
|
+
continue;
|
|
70145
|
+
}
|
|
69861
70146
|
let effectiveNode = resolveEffectiveNode(freshTask, settings);
|
|
69862
70147
|
schedulerLog.log(`Task ${task.id} routed to node=${effectiveNode.nodeId ?? "local"} (source=${effectiveNode.source})`);
|
|
69863
70148
|
if (effectiveNode.nodeId !== void 0 && this.options.nodeHealthMonitor) {
|
|
@@ -74995,6 +75280,36 @@ function execCommand(command, options) {
|
|
|
74995
75280
|
});
|
|
74996
75281
|
});
|
|
74997
75282
|
}
|
|
75283
|
+
function isInProcessBackupCommand(command) {
|
|
75284
|
+
if (!command) return false;
|
|
75285
|
+
const trimmed = command.trim();
|
|
75286
|
+
if (!trimmed) return false;
|
|
75287
|
+
if (SHELL_METACHARACTERS_REGEX.test(trimmed)) return false;
|
|
75288
|
+
const tokens = trimmed.split(/\s+/).map((tok) => tok.toLowerCase());
|
|
75289
|
+
let cursor = 0;
|
|
75290
|
+
if (tokens[cursor] === "npx") {
|
|
75291
|
+
cursor += 1;
|
|
75292
|
+
while (cursor < tokens.length) {
|
|
75293
|
+
const tok = tokens[cursor];
|
|
75294
|
+
if (tok === void 0 || !tok.startsWith("-")) break;
|
|
75295
|
+
const takesValue = (tok === "-p" || tok === "--package") && cursor + 1 < tokens.length && tokens[cursor + 1] !== void 0 && !tokens[cursor + 1].startsWith("-");
|
|
75296
|
+
cursor += takesValue ? 2 : 1;
|
|
75297
|
+
}
|
|
75298
|
+
}
|
|
75299
|
+
const binary = tokens[cursor];
|
|
75300
|
+
if (!binary || !FUSION_BINARY_TOKENS.has(binary)) return false;
|
|
75301
|
+
cursor += 1;
|
|
75302
|
+
if (tokens[cursor] !== "backup") return false;
|
|
75303
|
+
cursor += 1;
|
|
75304
|
+
if (tokens[cursor] !== "--create") return false;
|
|
75305
|
+
cursor += 1;
|
|
75306
|
+
for (; cursor < tokens.length; cursor += 1) {
|
|
75307
|
+
const tok = tokens[cursor];
|
|
75308
|
+
if (!tok) continue;
|
|
75309
|
+
if (!tok.startsWith("-")) return false;
|
|
75310
|
+
}
|
|
75311
|
+
return true;
|
|
75312
|
+
}
|
|
74998
75313
|
async function createAiPromptExecutor(cwd) {
|
|
74999
75314
|
const disposeLog = createLogger2("cron-runner");
|
|
75000
75315
|
return async (prompt, modelProvider, modelId) => {
|
|
@@ -75034,7 +75349,7 @@ function truncateOutput(stdout, stderr) {
|
|
|
75034
75349
|
}
|
|
75035
75350
|
return combined;
|
|
75036
75351
|
}
|
|
75037
|
-
var log14, DEFAULT_TIMEOUT_MS6, MAX_BUFFER, MAX_OUTPUT_LENGTH, DEFAULT_POLL_INTERVAL_MS, MIN_POLL_INTERVAL_MS, CronRunner, AI_AUTOMATION_SYSTEM_PROMPT;
|
|
75352
|
+
var log14, FUSION_BINARY_TOKENS, SHELL_METACHARACTERS_REGEX, DEFAULT_TIMEOUT_MS6, MAX_BUFFER, MAX_OUTPUT_LENGTH, DEFAULT_POLL_INTERVAL_MS, MIN_POLL_INTERVAL_MS, CronRunner, AI_AUTOMATION_SYSTEM_PROMPT;
|
|
75038
75353
|
var init_cron_runner = __esm({
|
|
75039
75354
|
"../engine/src/cron-runner.ts"() {
|
|
75040
75355
|
"use strict";
|
|
@@ -75043,6 +75358,14 @@ var init_cron_runner = __esm({
|
|
|
75043
75358
|
init_shell_utils();
|
|
75044
75359
|
init_pi();
|
|
75045
75360
|
log14 = createLogger2("cron-runner");
|
|
75361
|
+
FUSION_BINARY_TOKENS = /* @__PURE__ */ new Set([
|
|
75362
|
+
"fn",
|
|
75363
|
+
"fusion",
|
|
75364
|
+
"runfusion",
|
|
75365
|
+
"runfusion.ai",
|
|
75366
|
+
"@runfusion/fusion"
|
|
75367
|
+
]);
|
|
75368
|
+
SHELL_METACHARACTERS_REGEX = /[&|;<>`$()]/;
|
|
75046
75369
|
DEFAULT_TIMEOUT_MS6 = 5 * 60 * 1e3;
|
|
75047
75370
|
MAX_BUFFER = 1024 * 1024;
|
|
75048
75371
|
MAX_OUTPUT_LENGTH = 10 * 1024;
|
|
@@ -75183,6 +75506,9 @@ var init_cron_runner = __esm({
|
|
|
75183
75506
|
*/
|
|
75184
75507
|
async executeLegacyCommand(schedule, startedAt) {
|
|
75185
75508
|
log14.log(`Executing ${schedule.name} (${schedule.id}): ${schedule.command}`);
|
|
75509
|
+
if (isInProcessBackupCommand(schedule.command)) {
|
|
75510
|
+
return this.executeBackupInProcess(schedule, startedAt);
|
|
75511
|
+
}
|
|
75186
75512
|
try {
|
|
75187
75513
|
const timeoutMs = schedule.timeoutMs ?? DEFAULT_TIMEOUT_MS6;
|
|
75188
75514
|
const { stdout, stderr } = await execCommand(schedule.command, {
|
|
@@ -75213,6 +75539,47 @@ var init_cron_runner = __esm({
|
|
|
75213
75539
|
};
|
|
75214
75540
|
}
|
|
75215
75541
|
}
|
|
75542
|
+
/**
|
|
75543
|
+
* Run an auto-backup schedule in-process via the engine's open TaskStore,
|
|
75544
|
+
* bypassing the shell-out that would otherwise invoke an outdated fusion
|
|
75545
|
+
* binary on PATH. See `isInProcessBackupCommand` for the matching contract.
|
|
75546
|
+
*/
|
|
75547
|
+
async executeBackupInProcess(schedule, startedAt) {
|
|
75548
|
+
const action = await this.runBackupActionInProcess();
|
|
75549
|
+
if (action.success) {
|
|
75550
|
+
log14.log(`\u2713 ${schedule.name} completed in-process`);
|
|
75551
|
+
} else {
|
|
75552
|
+
log14.warn(`\u2717 ${schedule.name} in-process backup ${action.error ? `threw: ${action.error}` : `reported failure: ${action.output}`}`);
|
|
75553
|
+
}
|
|
75554
|
+
return {
|
|
75555
|
+
success: action.success,
|
|
75556
|
+
output: action.output,
|
|
75557
|
+
error: action.error,
|
|
75558
|
+
startedAt,
|
|
75559
|
+
completedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
75560
|
+
};
|
|
75561
|
+
}
|
|
75562
|
+
/**
|
|
75563
|
+
* Shared in-process backup execution used by both the legacy-command path
|
|
75564
|
+
* and the command-step path. Returns the success/output/error tuple in
|
|
75565
|
+
* a shape that callers can wrap into either a run or a step result.
|
|
75566
|
+
*/
|
|
75567
|
+
async runBackupActionInProcess() {
|
|
75568
|
+
try {
|
|
75569
|
+
const { runBackupCommand: runBackupCommand2 } = await Promise.resolve().then(() => (init_src(), src_exports));
|
|
75570
|
+
const fusionDir = this.store.getFusionDir();
|
|
75571
|
+
const settings = await this.store.getSettings();
|
|
75572
|
+
const result = await runBackupCommand2(fusionDir, settings);
|
|
75573
|
+
return {
|
|
75574
|
+
success: result.success,
|
|
75575
|
+
output: truncateOutput(result.output ?? "", ""),
|
|
75576
|
+
error: result.success ? void 0 : result.output
|
|
75577
|
+
};
|
|
75578
|
+
} catch (err) {
|
|
75579
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
75580
|
+
return { success: false, output: "", error: message };
|
|
75581
|
+
}
|
|
75582
|
+
}
|
|
75216
75583
|
/**
|
|
75217
75584
|
* Execute multiple steps sequentially.
|
|
75218
75585
|
* Aggregates per-step results into an overall AutomationRunResult.
|
|
@@ -75301,6 +75668,19 @@ var init_cron_runner = __esm({
|
|
|
75301
75668
|
completedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
75302
75669
|
};
|
|
75303
75670
|
}
|
|
75671
|
+
if (isInProcessBackupCommand(step.command)) {
|
|
75672
|
+
const action = await this.runBackupActionInProcess();
|
|
75673
|
+
return {
|
|
75674
|
+
stepId: step.id,
|
|
75675
|
+
stepName: step.name,
|
|
75676
|
+
stepIndex,
|
|
75677
|
+
success: action.success,
|
|
75678
|
+
output: action.output,
|
|
75679
|
+
error: action.error,
|
|
75680
|
+
startedAt,
|
|
75681
|
+
completedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
75682
|
+
};
|
|
75683
|
+
}
|
|
75304
75684
|
try {
|
|
75305
75685
|
const { stdout, stderr } = await execCommand(step.command, {
|
|
75306
75686
|
timeout: timeoutMs,
|
|
@@ -75492,6 +75872,7 @@ var init_routine_runner = __esm({
|
|
|
75492
75872
|
"../engine/src/routine-runner.ts"() {
|
|
75493
75873
|
"use strict";
|
|
75494
75874
|
import_cron_parser4 = __toESM(require_dist2(), 1);
|
|
75875
|
+
init_cron_runner();
|
|
75495
75876
|
init_logger2();
|
|
75496
75877
|
init_shell_utils();
|
|
75497
75878
|
log15 = createLogger2("routine-runner");
|
|
@@ -75649,6 +76030,30 @@ var init_routine_runner = __esm({
|
|
|
75649
76030
|
return this.executeCommand(routine.command ?? "", routine.timeoutMs, startedAt);
|
|
75650
76031
|
}
|
|
75651
76032
|
async executeCommand(command, timeoutMs, startedAt) {
|
|
76033
|
+
if (isInProcessBackupCommand(command) && this.options.taskStore) {
|
|
76034
|
+
try {
|
|
76035
|
+
const { runBackupCommand: runBackupCommand2 } = await Promise.resolve().then(() => (init_src(), src_exports));
|
|
76036
|
+
const fusionDir = this.options.taskStore.getFusionDir();
|
|
76037
|
+
const settings = await this.options.taskStore.getSettings();
|
|
76038
|
+
const result = await runBackupCommand2(fusionDir, settings);
|
|
76039
|
+
return {
|
|
76040
|
+
success: result.success,
|
|
76041
|
+
output: truncateOutput2(result.output ?? "", ""),
|
|
76042
|
+
error: result.success ? void 0 : result.output,
|
|
76043
|
+
startedAt,
|
|
76044
|
+
completedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
76045
|
+
};
|
|
76046
|
+
} catch (err) {
|
|
76047
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
76048
|
+
return {
|
|
76049
|
+
success: false,
|
|
76050
|
+
output: "",
|
|
76051
|
+
error: message,
|
|
76052
|
+
startedAt,
|
|
76053
|
+
completedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
76054
|
+
};
|
|
76055
|
+
}
|
|
76056
|
+
}
|
|
75652
76057
|
try {
|
|
75653
76058
|
const { stdout, stderr } = await execAsync6(command, {
|
|
75654
76059
|
timeout: timeoutMs ?? DEFAULT_TIMEOUT_MS7,
|
|
@@ -76440,6 +76845,13 @@ var init_self_healing = __esm({
|
|
|
76440
76845
|
* stale in-progress/planning tasks that no longer have a live worker.
|
|
76441
76846
|
*/
|
|
76442
76847
|
async runStartupRecovery() {
|
|
76848
|
+
const settings = await this.store.getSettings();
|
|
76849
|
+
if (settings.globalPause || settings.enginePaused) {
|
|
76850
|
+
log16.log(
|
|
76851
|
+
`Startup recovery skipped \u2014 ${settings.globalPause ? "global pause" : "engine pause"} is active`
|
|
76852
|
+
);
|
|
76853
|
+
return;
|
|
76854
|
+
}
|
|
76443
76855
|
const steps = [
|
|
76444
76856
|
{ name: "no-progress-no-task-done", fn: () => this.recoverNoProgressNoTaskDoneFailures().then(() => void 0) },
|
|
76445
76857
|
{ name: "completed-tasks", fn: () => this.recoverCompletedTasks().then(() => void 0) },
|
|
@@ -76790,27 +77202,34 @@ var init_self_healing = __esm({
|
|
|
76790
77202
|
log16.error(`Maintenance batch 1 step "${fn.name}" failed: ${stepErr instanceof Error ? stepErr.message : String(stepErr)}`);
|
|
76791
77203
|
}
|
|
76792
77204
|
}
|
|
76793
|
-
const
|
|
76794
|
-
|
|
76795
|
-
|
|
76796
|
-
|
|
76797
|
-
|
|
76798
|
-
|
|
76799
|
-
|
|
76800
|
-
|
|
76801
|
-
|
|
76802
|
-
|
|
76803
|
-
|
|
76804
|
-
|
|
76805
|
-
|
|
76806
|
-
|
|
76807
|
-
|
|
76808
|
-
|
|
76809
|
-
|
|
76810
|
-
|
|
76811
|
-
|
|
76812
|
-
|
|
76813
|
-
|
|
77205
|
+
const recoverySettings = await this.store.getSettings();
|
|
77206
|
+
if (recoverySettings.globalPause || recoverySettings.enginePaused) {
|
|
77207
|
+
log16.log(
|
|
77208
|
+
`Maintenance batch 2 skipped \u2014 ${recoverySettings.globalPause ? "global pause" : "engine pause"} is active`
|
|
77209
|
+
);
|
|
77210
|
+
} else {
|
|
77211
|
+
const batch2Fns = [
|
|
77212
|
+
{ name: "recover-completed-tasks", fn: () => this.recoverCompletedTasks() },
|
|
77213
|
+
{ name: "recover-stale-incomplete-review", fn: () => this.recoverStaleIncompleteReviewTasks() },
|
|
77214
|
+
{ name: "recover-failed-pre-merge-steps", fn: () => this.recoverReviewTasksWithFailedPreMergeSteps() },
|
|
77215
|
+
{ name: "recover-interrupted-merging", fn: () => this.recoverInterruptedMergingTasks() },
|
|
77216
|
+
{ name: "recover-mergeable-review", fn: () => this.recoverMergeableReviewTasks() },
|
|
77217
|
+
{ name: "recover-merged-review", fn: () => this.recoverMergedReviewTasks() },
|
|
77218
|
+
{ name: "recover-misclassified-failures", fn: () => this.recoverMisclassifiedFailures() },
|
|
77219
|
+
{ name: "recover-no-progress-no-task-done", fn: () => this.recoverNoProgressNoTaskDoneFailures() },
|
|
77220
|
+
{ name: "recover-partial-progress-no-task-done", fn: () => this.recoverPartialProgressNoTaskDoneFailures() },
|
|
77221
|
+
{ name: "recover-orphaned-executions", fn: () => this.recoverOrphanedExecutions() },
|
|
77222
|
+
{ name: "recover-approved-triage", fn: () => this.recoverApprovedTriageTasks() },
|
|
77223
|
+
{ name: "recover-orphaned-planning", fn: () => this.recoverOrphanedPlanningTasks() },
|
|
77224
|
+
{ name: "recover-ghost-review", fn: () => this.recoverGhostReviewTasks() }
|
|
77225
|
+
];
|
|
77226
|
+
for (const fn of batch2Fns) {
|
|
77227
|
+
try {
|
|
77228
|
+
await fn.fn();
|
|
77229
|
+
log16.log(`Maintenance batch 2 step "${fn.name}" succeeded`);
|
|
77230
|
+
} catch (stepErr) {
|
|
77231
|
+
log16.error(`Maintenance batch 2 step "${fn.name}" failed: ${stepErr instanceof Error ? stepErr.message : String(stepErr)}`);
|
|
77232
|
+
}
|
|
76814
77233
|
}
|
|
76815
77234
|
}
|
|
76816
77235
|
const batch3Fns = [
|
|
@@ -78543,6 +78962,10 @@ var init_in_process_runtime = __esm({
|
|
|
78543
78962
|
* before `start()` via `setMergeEnqueuer`.
|
|
78544
78963
|
*/
|
|
78545
78964
|
mergeEnqueuer;
|
|
78965
|
+
/** Tracks whether startup recovery was intentionally deferred due to pause state. */
|
|
78966
|
+
startupRecoveryDeferred = false;
|
|
78967
|
+
/** Prevent duplicate unpause recovery dispatches from racing each other. */
|
|
78968
|
+
resumeAfterUnpauseRunning = false;
|
|
78546
78969
|
/**
|
|
78547
78970
|
* Start the runtime and initialize all subsystems.
|
|
78548
78971
|
*
|
|
@@ -78576,7 +78999,7 @@ var init_in_process_runtime = __esm({
|
|
|
78576
78999
|
runtimeLog.log(`TaskStore initialized for project ${this.config.projectId}`);
|
|
78577
79000
|
}
|
|
78578
79001
|
this.messageStore = new MessageStoreClass(this.taskStore.getDatabase());
|
|
78579
|
-
this.pluginStore = new PluginStoreClass(this.
|
|
79002
|
+
this.pluginStore = new PluginStoreClass(this.config.workingDirectory);
|
|
78580
79003
|
await this.pluginStore.init();
|
|
78581
79004
|
this.pluginLoader = new PluginLoaderClass({
|
|
78582
79005
|
pluginStore: this.pluginStore,
|
|
@@ -78968,11 +79391,16 @@ var init_in_process_runtime = __esm({
|
|
|
78968
79391
|
this.selfHealingManager.start();
|
|
78969
79392
|
this.stuckTaskDetector.start();
|
|
78970
79393
|
this.setupEventForwarding();
|
|
78971
|
-
await this.
|
|
78972
|
-
|
|
78973
|
-
|
|
78974
|
-
runtimeLog.
|
|
78975
|
-
|
|
79394
|
+
const startupSettings = await this.taskStore.getSettings();
|
|
79395
|
+
if (startupSettings.globalPause || startupSettings.enginePaused) {
|
|
79396
|
+
this.startupRecoveryDeferred = true;
|
|
79397
|
+
runtimeLog.log(
|
|
79398
|
+
`Startup recovery deferred \u2014 ${startupSettings.globalPause ? "global pause" : "engine pause"} is active`
|
|
79399
|
+
);
|
|
79400
|
+
} else {
|
|
79401
|
+
this.startupRecoveryDeferred = false;
|
|
79402
|
+
await this.resumeStartupRecoverySequence();
|
|
79403
|
+
}
|
|
78976
79404
|
this.scheduler.start();
|
|
78977
79405
|
this.triageProcessor?.start();
|
|
78978
79406
|
this.missionExecutionLoop = missionExecutionLoop;
|
|
@@ -79138,6 +79566,45 @@ var init_in_process_runtime = __esm({
|
|
|
79138
79566
|
setMergeEnqueuer(enqueueMerge) {
|
|
79139
79567
|
this.mergeEnqueuer = enqueueMerge;
|
|
79140
79568
|
}
|
|
79569
|
+
/**
|
|
79570
|
+
* Resume executor/self-healing activity after an unpause transition.
|
|
79571
|
+
*
|
|
79572
|
+
* When startup recovery had been deferred, this replays the original startup
|
|
79573
|
+
* ordering so orphan resume and self-healing cannot race each other.
|
|
79574
|
+
*/
|
|
79575
|
+
async resumeAfterUnpause() {
|
|
79576
|
+
if (!this.taskStore || !this.executor || !this.selfHealingManager) {
|
|
79577
|
+
return;
|
|
79578
|
+
}
|
|
79579
|
+
if (this.resumeAfterUnpauseRunning) {
|
|
79580
|
+
return;
|
|
79581
|
+
}
|
|
79582
|
+
this.resumeAfterUnpauseRunning = true;
|
|
79583
|
+
try {
|
|
79584
|
+
const settings = await this.taskStore.getSettings();
|
|
79585
|
+
if (settings.globalPause || settings.enginePaused) {
|
|
79586
|
+
runtimeLog.log(
|
|
79587
|
+
`Unpause recovery still blocked \u2014 ${settings.globalPause ? "global pause" : "engine pause"} remains active`
|
|
79588
|
+
);
|
|
79589
|
+
return;
|
|
79590
|
+
}
|
|
79591
|
+
if (this.startupRecoveryDeferred) {
|
|
79592
|
+
await this.resumeStartupRecoverySequence();
|
|
79593
|
+
this.startupRecoveryDeferred = false;
|
|
79594
|
+
return;
|
|
79595
|
+
}
|
|
79596
|
+
await this.executor.resumeOrphaned();
|
|
79597
|
+
} finally {
|
|
79598
|
+
this.resumeAfterUnpauseRunning = false;
|
|
79599
|
+
}
|
|
79600
|
+
}
|
|
79601
|
+
async resumeStartupRecoverySequence() {
|
|
79602
|
+
await this.selfHealingManager.recoverNoProgressNoTaskDoneFailures();
|
|
79603
|
+
await this.executor.resumeOrphaned();
|
|
79604
|
+
void this.selfHealingManager.runStartupRecovery().catch((err) => {
|
|
79605
|
+
runtimeLog.error("Self-healing startup recovery failed:", err);
|
|
79606
|
+
});
|
|
79607
|
+
}
|
|
79141
79608
|
/**
|
|
79142
79609
|
* Get the project's TaskStore instance.
|
|
79143
79610
|
* @throws Error if runtime has not been started
|
|
@@ -82977,13 +83444,13 @@ ${detail}`
|
|
|
82977
83444
|
if (prev.globalPause && !s.globalPause) {
|
|
82978
83445
|
runtimeLog.log("Global unpause \u2014 resuming agentic activity");
|
|
82979
83446
|
try {
|
|
82980
|
-
const
|
|
82981
|
-
|
|
82982
|
-
(err) => runtimeLog.error("Failed to resume
|
|
83447
|
+
const runtime = this.runtime;
|
|
83448
|
+
runtime.resumeAfterUnpause?.().catch(
|
|
83449
|
+
(err) => runtimeLog.error("Failed to resume agentic activity on unpause:", err)
|
|
82983
83450
|
);
|
|
82984
83451
|
} catch (err) {
|
|
82985
83452
|
runtimeLog.warn(
|
|
82986
|
-
`Global unpause: failed to dispatch
|
|
83453
|
+
`Global unpause: failed to dispatch resumeAfterUnpause: ${err instanceof Error ? err.message : String(err)}`
|
|
82987
83454
|
);
|
|
82988
83455
|
}
|
|
82989
83456
|
if (s.autoMerge) {
|
|
@@ -83007,13 +83474,13 @@ ${detail}`
|
|
|
83007
83474
|
if (prev.enginePaused && !s.enginePaused) {
|
|
83008
83475
|
runtimeLog.log("Engine unpaused \u2014 resuming agentic activity");
|
|
83009
83476
|
try {
|
|
83010
|
-
const
|
|
83011
|
-
|
|
83012
|
-
(err) => runtimeLog.error("Failed to resume
|
|
83477
|
+
const runtime = this.runtime;
|
|
83478
|
+
runtime.resumeAfterUnpause?.().catch(
|
|
83479
|
+
(err) => runtimeLog.error("Failed to resume agentic activity on engine unpause:", err)
|
|
83013
83480
|
);
|
|
83014
83481
|
} catch (err) {
|
|
83015
83482
|
runtimeLog.warn(
|
|
83016
|
-
`Engine unpause: failed to dispatch
|
|
83483
|
+
`Engine unpause: failed to dispatch resumeAfterUnpause: ${err instanceof Error ? err.message : String(err)}`
|
|
83017
83484
|
);
|
|
83018
83485
|
}
|
|
83019
83486
|
if (s.autoMerge) {
|