

























11// Human-facing background task commands.
22// Handles task listing/show/cancel/notify/audit plus registry maintenance for tasks, flows, and sessions.
334-import fs from "node:fs";
54import { timestampMsToIsoString } from "@openclaw/normalization-core/number-coercion";
65import { normalizeOptionalString } from "@openclaw/normalization-core/string-coerce";
76import { isRich, theme } from "../../packages/terminal-core/src/theme.js";
87import { formatCliCommand } from "../cli/command-format.js";
98import { formatLookupMiss } from "../cli/error-format.js";
109import { getRuntimeConfig } from "../config/config.js";
1110import {
12-loadSessionStore,
13-pruneStaleEntries,
1411resolveAllAgentSessionStoreTargetsSync,
15-updateSessionStore,
16-type SessionEntry,
12+runSessionRegistryMaintenanceForStore,
1713} from "../config/sessions.js";
1814import { loadCronJobsStoreSync, resolveCronJobsStorePath } from "../cron/store.js";
1915import type { RuntimeEnv } from "../runtime.js";
20-import { parseAgentSessionKey } from "../sessions/session-key-utils.js";
2116import { getTaskById, updateTaskNotifyPolicyById } from "../tasks/runtime-internal.js";
2217import { cancelDetachedTaskRunById } from "../tasks/task-executor.js";
2318import { listTaskFlowAuditFindings } from "../tasks/task-flow-registry.audit.js";
@@ -133,14 +128,6 @@ type SessionRegistryMaintenanceSummary = {
133128stores: SessionRegistryMaintenanceStoreSummary[];
134129};
135130136-function parseCronRunSessionJobId(sessionKey: string): string | undefined {
137-const parsed = parseAgentSessionKey(sessionKey);
138-if (!parsed) {
139-return undefined;
140-}
141-return /^cron:([^:]+):run:[^:]+$/u.exec(parsed.rest)?.[1];
142-}
143-144131function readRunningCronJobIds(): Set<string> {
145132try {
146133const cronStorePath = resolveCronJobsStorePath(getRuntimeConfig().cron?.store);
@@ -155,95 +142,26 @@ function readRunningCronJobIds(): Set<string> {
155142}
156143}
157144158-function buildSessionRegistryPreserveKeys(params: {
159-store: Record<string, SessionEntry>;
160-runningCronJobIds: ReadonlySet<string>;
161-}): { preserveKeys: Set<string>; preservedRunning: number } {
162-const preserveKeys = new Set<string>();
163-let preservedRunning = 0;
164-for (const key of Object.keys(params.store)) {
165-const jobId = parseCronRunSessionJobId(key);
166-if (!jobId) {
167-// Non-cron session rows are outside this maintenance pass; preserve them.
168-preserveKeys.add(key);
169-continue;
170-}
171-if (params.runningCronJobIds.has(jobId)) {
172-preserveKeys.add(key);
173-preservedRunning += 1;
174-}
175-}
176-return { preserveKeys, preservedRunning };
177-}
178-179145async function runSessionRegistryMaintenance(params: {
180146apply: boolean;
181147}): Promise<SessionRegistryMaintenanceSummary> {
182148const cfg = getRuntimeConfig();
183149const runningCronJobIds = readRunningCronJobIds();
184150const stores: SessionRegistryMaintenanceStoreSummary[] = [];
185151for (const target of resolveAllAgentSessionStoreTargetsSync(cfg)) {
186-if (!fs.existsSync(target.storePath)) {
187-stores.push({
188-agentId: target.agentId,
189-storePath: target.storePath,
190-beforeCount: 0,
191-afterCount: 0,
192-pruned: 0,
193-preservedRunning: 0,
194-});
195-continue;
196-}
197-const beforeStore = loadSessionStore(target.storePath, { skipCache: true });
198-const beforeCount = Object.keys(beforeStore).length;
199-if (params.apply) {
200-// Apply mode mutates each store atomically through updateSessionStore.
201-const applied = await updateSessionStore(
202-target.storePath,
203-(store) => {
204-const { preserveKeys, preservedRunning } = buildSessionRegistryPreserveKeys({
205- store,
206- runningCronJobIds,
207-});
208-const pruned = pruneStaleEntries(store, SESSION_REGISTRY_RETENTION_MS, {
209-log: false,
210- preserveKeys,
211-});
212-return {
213- pruned,
214-afterCount: Object.keys(store).length,
215- preservedRunning,
216-};
217-},
218-{ skipMaintenance: true },
219-);
220-stores.push({
221-agentId: target.agentId,
222-storePath: target.storePath,
223- beforeCount,
224-afterCount: applied.afterCount,
225-pruned: applied.pruned,
226-preservedRunning: applied.preservedRunning,
227-});
228-continue;
229-}
230-const previewStore = structuredClone(beforeStore);
231-// Preview mode runs pruning against a clone so dry-run output cannot change stores.
232-const { preserveKeys, preservedRunning } = buildSessionRegistryPreserveKeys({
233-store: previewStore,
152+const result = await runSessionRegistryMaintenanceForStore({
153+apply: params.apply,
154+retentionMs: SESSION_REGISTRY_RETENTION_MS,
234155 runningCronJobIds,
235-});
236-const pruned = pruneStaleEntries(previewStore, SESSION_REGISTRY_RETENTION_MS, {
237-log: false,
238- preserveKeys,
156+storePath: target.storePath,
239157});
240158stores.push({
241159agentId: target.agentId,
242160storePath: target.storePath,
243- beforeCount,
244-afterCount: Object.keys(previewStore).length,
245- pruned,
246- preservedRunning,
161+beforeCount: result.beforeCount,
162+afterCount: result.afterCount,
163+pruned: result.pruned,
164+preservedRunning: result.preservedRunning,
247165});
248166}
249167return {
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。