mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-08 03:10:45 +00:00
fix(subagents): honor archiveAfterMinutes for session-mode reaping (#78263)
Merged via squash.
Prepared head SHA: b415467008
Co-authored-by: arniesaha <3646287+arniesaha@users.noreply.github.com>
Co-authored-by: jalehman <550978+jalehman@users.noreply.github.com>
Reviewed-by: @jalehman
This commit is contained in:
@@ -480,6 +480,7 @@ Docs: https://docs.openclaw.ai
|
||||
- Doctor/plugins: discover doctor contracts from load-path channel plugins during `openclaw doctor --fix`, so plugin-owned legacy config repair runs before validation. (#77477) Thanks @jalehman.
|
||||
- Dependencies: bump transitive `basic-ftp` to 5.3.1 so the runtime lockfile no longer includes the vulnerable 5.3.0 build flagged by the production dependency audit. (#78637) Thanks @sallyom.
|
||||
- Agents/compaction: clamp compaction summary reserve tokens to each model's output limit so high-context compaction no longer requests invalid `max_tokens` values. (#54392) Thanks @adzendo.
|
||||
- Agents/subagents: have completed session-mode subagent registry rows honor `agents.defaults.subagents.archiveAfterMinutes` (default 60 minutes; same knob run-mode already uses for `archiveAtMs`) instead of a hardcoded 5-minute TTL, so `subagents list` and other registry-backed surfaces still show recently-completed runs and operators have one consistent retention knob across spawn modes. (#78263) Thanks @arniesaha.
|
||||
|
||||
## 2026.5.3-1
|
||||
|
||||
|
||||
@@ -893,6 +893,14 @@ describe("subagent registry seam flow", () => {
|
||||
|
||||
it("passes stored agentDir through swept context-engine cleanup paths", async () => {
|
||||
const now = Date.parse("2026-03-24T12:00:00Z");
|
||||
// Session-mode reaping now honors agents.defaults.subagents.archiveAfterMinutes
|
||||
// (same knob run-mode uses for archiveAtMs). The default-config mock above sets
|
||||
// archiveAfterMinutes: 0, which disables session-mode reaping; opt this test
|
||||
// into a real retention window so the swept-cleanup path still fires.
|
||||
mocks.getRuntimeConfig.mockReturnValueOnce({
|
||||
agents: { defaults: { subagents: { archiveAfterMinutes: 1 } } },
|
||||
session: { mainKey: "main", scope: "per-sender" as const },
|
||||
});
|
||||
mod.addSubagentRunForTests({
|
||||
runId: "run-session-swept-context-engine",
|
||||
childSessionKey: "agent:alt:session:child-session",
|
||||
|
||||
@@ -36,6 +36,7 @@ import {
|
||||
reconcileOrphanedRestoredRuns,
|
||||
reconcileOrphanedRun,
|
||||
resolveAnnounceRetryDelayMs,
|
||||
resolveArchiveAfterMs,
|
||||
resolveSubagentRunOrphanReason,
|
||||
resolveSubagentSessionStatus,
|
||||
safeRemoveAttachmentsDir,
|
||||
@@ -196,8 +197,6 @@ const LIFECYCLE_ERROR_RETRY_GRACE_MS = 15_000;
|
||||
* `timed out` completion right before the eventual success.
|
||||
*/
|
||||
const LIFECYCLE_TIMEOUT_RETRY_GRACE_MS = 15_000;
|
||||
/** Absolute TTL for session-mode runs after cleanup completes (no archiveAtMs). */
|
||||
const SESSION_RUN_TTL_MS = 5 * 60_000; // 5 minutes
|
||||
/** Absolute TTL for orphaned pendingLifecycleError / pendingLifecycleTimeout entries. */
|
||||
const PENDING_LIFECYCLE_TERMINAL_TTL_MS = 5 * 60_000; // 5 minutes
|
||||
/** Grace period before treating a "running" subagent without a live run context as stale. */
|
||||
@@ -751,6 +750,7 @@ async function sweepSubagentRuns() {
|
||||
try {
|
||||
const now = Date.now();
|
||||
const storeCache = new Map<string, Record<string, SessionEntry>>();
|
||||
const sessionRetentionMs = resolveArchiveAfterMs(subagentRegistryDeps.getRuntimeConfig());
|
||||
let mutated = false;
|
||||
for (const [runId, entry] of subagentRuns.entries()) {
|
||||
if (typeof entry.endedAt !== "number") {
|
||||
@@ -813,12 +813,18 @@ async function sweepSubagentRuns() {
|
||||
}
|
||||
}
|
||||
|
||||
// Session-mode runs have no archiveAtMs — apply absolute TTL after cleanup completes.
|
||||
// Session-mode runs have no archiveAtMs because the child session is retained
|
||||
// independently — but the registry row itself still needs to be reaped after
|
||||
// cleanup, otherwise `subagents list` and other registry-backed surfaces grow
|
||||
// without bound. Honor the same `agents.defaults.subagents.archiveAfterMinutes`
|
||||
// window run-mode uses for `archiveAtMs`, so operators get one consistent
|
||||
// retention knob (default 60 minutes; 0 disables session-mode reaping).
|
||||
// Use cleanupCompletedAt (not endedAt) to avoid interrupting deferred cleanup flows.
|
||||
if (!entry.archiveAtMs) {
|
||||
if (
|
||||
typeof sessionRetentionMs === "number" &&
|
||||
typeof entry.cleanupCompletedAt === "number" &&
|
||||
now - entry.cleanupCompletedAt > SESSION_RUN_TTL_MS
|
||||
now - entry.cleanupCompletedAt > sessionRetentionMs
|
||||
) {
|
||||
clearPendingLifecycleError(runId);
|
||||
void notifyContextEngineSubagentEnded({
|
||||
|
||||
Reference in New Issue
Block a user