mirror of
https://github.com/openclaw/openclaw.git
synced 2026-07-09 15:59:30 +00:00
Fix silent context-engine maintenance task delivery status (#102147)
* Fix silent context maintenance task delivery status * refactor(context-engine): document maintenance delivery invariant * fix(context-engine): preserve visible maintenance flows * style(context-engine): format maintenance test import * test(context-engine): use direct maintenance task lookup --------- Co-authored-by: Sascha Kuhlmann <coolmanns@users.noreply.github.com> Co-authored-by: Peter Steinberger <steipete@gmail.com>
This commit is contained in:
parent
3ebcfb8ee3
commit
13821c9d34
2 changed files with 50 additions and 46 deletions
|
|
@ -9,7 +9,7 @@ import {
|
||||||
} from "../../process/command-queue.js";
|
} from "../../process/command-queue.js";
|
||||||
import * as commandQueueModule from "../../process/command-queue.js";
|
import * as commandQueueModule from "../../process/command-queue.js";
|
||||||
import { createQueuedTaskRun as createQueuedTaskRunOrNull } from "../../tasks/task-executor.js";
|
import { createQueuedTaskRun as createQueuedTaskRunOrNull } from "../../tasks/task-executor.js";
|
||||||
import { resetTaskFlowRegistryForTests } from "../../tasks/task-flow-registry.js";
|
import { getTaskFlowById, resetTaskFlowRegistryForTests } from "../../tasks/task-flow-registry.js";
|
||||||
import {
|
import {
|
||||||
getTaskById,
|
getTaskById,
|
||||||
listTasksForOwnerKey,
|
listTasksForOwnerKey,
|
||||||
|
|
@ -654,7 +654,7 @@ describe("runContextEngineMaintenance", () => {
|
||||||
requesterSessionKey: sessionKey,
|
requesterSessionKey: sessionKey,
|
||||||
taskKind: TURN_MAINTENANCE_TASK_KIND,
|
taskKind: TURN_MAINTENANCE_TASK_KIND,
|
||||||
notifyPolicy: "silent",
|
notifyPolicy: "silent",
|
||||||
deliveryStatus: "pending",
|
deliveryStatus: "not_applicable",
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!releaseForeground) {
|
if (!releaseForeground) {
|
||||||
|
|
@ -1494,6 +1494,21 @@ describe("runContextEngineMaintenance", () => {
|
||||||
await waitForAssertion(() => expect(maintain).toHaveBeenCalledTimes(1));
|
await waitForAssertion(() => expect(maintain).toHaveBeenCalledTimes(1));
|
||||||
expect(sendMessageMock).not.toHaveBeenCalled();
|
expect(sendMessageMock).not.toHaveBeenCalled();
|
||||||
expect(peekSystemEvents(sessionKey)).toStrictEqual([]);
|
expect(peekSystemEvents(sessionKey)).toStrictEqual([]);
|
||||||
|
|
||||||
|
const tasks = listTasksForOwnerKey(sessionKey).filter(
|
||||||
|
(task) => task.taskKind === TURN_MAINTENANCE_TASK_KIND,
|
||||||
|
);
|
||||||
|
expect(tasks).toHaveLength(1);
|
||||||
|
await waitForAssertion(() =>
|
||||||
|
expect(getTaskById(tasks[0].taskId)?.status).toBe("succeeded"),
|
||||||
|
);
|
||||||
|
const task = requireRecord(getTaskById(tasks[0].taskId), "maintenance task");
|
||||||
|
expectRecordFields(task, {
|
||||||
|
status: "succeeded",
|
||||||
|
notifyPolicy: "silent",
|
||||||
|
deliveryStatus: "not_applicable",
|
||||||
|
});
|
||||||
|
expect(task.parentFlowId).toBeUndefined();
|
||||||
} finally {
|
} finally {
|
||||||
vi.useRealTimers();
|
vi.useRealTimers();
|
||||||
}
|
}
|
||||||
|
|
@ -1552,6 +1567,14 @@ describe("runContextEngineMaintenance", () => {
|
||||||
"Background task update: Context engine turn maintenance.",
|
"Background task update: Context engine turn maintenance.",
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
const task = listTasksForOwnerKey(sessionKey).find(
|
||||||
|
(candidate) => candidate.taskKind === TURN_MAINTENANCE_TASK_KIND,
|
||||||
|
);
|
||||||
|
const parentFlowId = task?.parentFlowId;
|
||||||
|
if (!parentFlowId) {
|
||||||
|
throw new Error("Expected visible maintenance to have a task flow");
|
||||||
|
}
|
||||||
|
expect(getTaskFlowById(parentFlowId)?.status).toBe("running");
|
||||||
|
|
||||||
if (!releaseMaintenance) {
|
if (!releaseMaintenance) {
|
||||||
throw new Error("Expected maintenance release callback to be initialized");
|
throw new Error("Expected maintenance release callback to be initialized");
|
||||||
|
|
@ -1563,6 +1586,7 @@ describe("runContextEngineMaintenance", () => {
|
||||||
"Background task done: Context engine turn maintenance",
|
"Background task done: Context engine turn maintenance",
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
expect(getTaskFlowById(parentFlowId)?.status).toBe("succeeded");
|
||||||
} finally {
|
} finally {
|
||||||
vi.useRealTimers();
|
vi.useRealTimers();
|
||||||
}
|
}
|
||||||
|
|
@ -1609,6 +1633,14 @@ describe("runContextEngineMaintenance", () => {
|
||||||
"Background task failed: Context engine turn maintenance",
|
"Background task failed: Context engine turn maintenance",
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
const task = listTasksForOwnerKey(sessionKey).find(
|
||||||
|
(candidate) => candidate.taskKind === TURN_MAINTENANCE_TASK_KIND,
|
||||||
|
);
|
||||||
|
const parentFlowId = task?.parentFlowId;
|
||||||
|
if (!parentFlowId) {
|
||||||
|
throw new Error("Expected failed maintenance to have a task flow");
|
||||||
|
}
|
||||||
|
expect(getTaskFlowById(parentFlowId)?.status).toBe("failed");
|
||||||
} finally {
|
} finally {
|
||||||
vi.useRealTimers();
|
vi.useRealTimers();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,6 @@ import {
|
||||||
createQueuedTaskRun,
|
createQueuedTaskRun,
|
||||||
failTaskRunByRunId,
|
failTaskRunByRunId,
|
||||||
recordTaskRunProgressByRunId,
|
recordTaskRunProgressByRunId,
|
||||||
setDetachedTaskDeliveryStatusByRunId,
|
|
||||||
startTaskRunByRunId,
|
startTaskRunByRunId,
|
||||||
} from "../../tasks/detached-task-runtime.js";
|
} from "../../tasks/detached-task-runtime.js";
|
||||||
import {
|
import {
|
||||||
|
|
@ -231,11 +230,15 @@ function markDeferredTurnMaintenanceTaskScheduleFailure(params: {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function buildTurnMaintenanceTaskDescriptor(params: { sessionKey: string }) {
|
function buildTurnMaintenanceTaskDescriptor(params: {
|
||||||
const runId = `turn-maint:${params.sessionKey}:${Date.now().toString(36)}:${randomUUID().slice(
|
sessionKey: string;
|
||||||
0,
|
runId?: string;
|
||||||
8,
|
notifyPolicy?: "silent" | "done_only" | "state_changes";
|
||||||
)}`;
|
deliveryStatus?: "not_applicable" | "pending";
|
||||||
|
}) {
|
||||||
|
const runId =
|
||||||
|
params.runId ??
|
||||||
|
`turn-maint:${params.sessionKey}:${Date.now().toString(36)}:${randomUUID().slice(0, 8)}`;
|
||||||
return createQueuedTaskRun({
|
return createQueuedTaskRun({
|
||||||
runtime: "acp",
|
runtime: "acp",
|
||||||
taskKind: TURN_MAINTENANCE_TASK_KIND,
|
taskKind: TURN_MAINTENANCE_TASK_KIND,
|
||||||
|
|
@ -246,8 +249,10 @@ function buildTurnMaintenanceTaskDescriptor(params: { sessionKey: string }) {
|
||||||
runId,
|
runId,
|
||||||
label: TURN_MAINTENANCE_TASK_LABEL,
|
label: TURN_MAINTENANCE_TASK_LABEL,
|
||||||
task: TURN_MAINTENANCE_TASK_TASK,
|
task: TURN_MAINTENANCE_TASK_TASK,
|
||||||
notifyPolicy: "silent",
|
notifyPolicy: params.notifyPolicy ?? "silent",
|
||||||
deliveryStatus: "pending",
|
// Fast maintenance stays silent and must not create a one-task flow.
|
||||||
|
// Long-running and failed workers promote it to pending before notifying.
|
||||||
|
deliveryStatus: params.deliveryStatus ?? "not_applicable",
|
||||||
preferMetadata: true,
|
preferMetadata: true,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -257,45 +262,12 @@ function promoteTurnMaintenanceTaskVisibility(params: {
|
||||||
runId: string;
|
runId: string;
|
||||||
notifyPolicy: "done_only" | "state_changes";
|
notifyPolicy: "done_only" | "state_changes";
|
||||||
}) {
|
}) {
|
||||||
const task = findTaskByRunIdForOwner({
|
return buildTurnMaintenanceTaskDescriptor({
|
||||||
runId: params.runId,
|
|
||||||
callerOwnerKey: params.sessionKey,
|
|
||||||
});
|
|
||||||
if (!task) {
|
|
||||||
return createQueuedTaskRun({
|
|
||||||
runtime: "acp",
|
|
||||||
taskKind: TURN_MAINTENANCE_TASK_KIND,
|
|
||||||
sourceId: TURN_MAINTENANCE_TASK_KIND,
|
|
||||||
requesterSessionKey: params.sessionKey,
|
|
||||||
ownerKey: params.sessionKey,
|
|
||||||
scopeKind: "session",
|
|
||||||
runId: params.runId,
|
|
||||||
label: TURN_MAINTENANCE_TASK_LABEL,
|
|
||||||
task: TURN_MAINTENANCE_TASK_TASK,
|
|
||||||
notifyPolicy: params.notifyPolicy,
|
|
||||||
deliveryStatus: "pending",
|
|
||||||
preferMetadata: true,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
setDetachedTaskDeliveryStatusByRunId({
|
|
||||||
runId: params.runId,
|
|
||||||
runtime: "acp",
|
|
||||||
sessionKey: params.sessionKey,
|
sessionKey: params.sessionKey,
|
||||||
|
runId: params.runId,
|
||||||
|
notifyPolicy: params.notifyPolicy,
|
||||||
deliveryStatus: "pending",
|
deliveryStatus: "pending",
|
||||||
});
|
});
|
||||||
if (task.notifyPolicy !== params.notifyPolicy) {
|
|
||||||
updateTaskNotifyPolicyForOwner({
|
|
||||||
taskId: task.taskId,
|
|
||||||
callerOwnerKey: params.sessionKey,
|
|
||||||
notifyPolicy: params.notifyPolicy,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return (
|
|
||||||
findTaskByRunIdForOwner({
|
|
||||||
runId: params.runId,
|
|
||||||
callerOwnerKey: params.sessionKey,
|
|
||||||
}) ?? task
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue