























@@ -4,6 +4,7 @@
44 * It can delegate cleanup to a live gateway or run local store maintenance,
55 * with dry-run tables that explain every planned pruning action.
66 */
7+import { sanitizeTerminalText } from "../../packages/terminal-core/src/safe-text.js";
78import { isRich, theme } from "../../packages/terminal-core/src/theme.js";
89import { getRuntimeConfig } from "../config/config.js";
910import {
@@ -35,6 +36,13 @@ const ACTION_PAD = 16;
35363637type SessionCleanupActionRow = ReturnType<typeof toSessionDisplayRows>[number] & {
3738action: ReturnType<typeof resolveSessionCleanupAction>;
39+label?: string;
40+};
41+42+type SessionCleanupLabelSummary = {
43+label: string;
44+kept: number;
45+pruned: number;
3846};
39474048function formatCleanupActionCell(
@@ -75,6 +83,7 @@ function buildActionRows(params: {
7583// action labels as the cleanup engine without mutating the preview store.
7684return toSessionDisplayRows(params.beforeStore).map((row) =>
7785Object.assign({}, row, {
86+label: params.beforeStore[row.key]?.label,
7887action: resolveSessionCleanupAction({
7988key: row.key,
8089missingKeys: params.missingKeys,
@@ -87,6 +96,46 @@ function buildActionRows(params: {
8796);
8897}
899899+function buildLabelSummaries(actionRows: SessionCleanupActionRow[]): SessionCleanupLabelSummary[] {
100+const summaryByLabel = new Map<string, SessionCleanupLabelSummary>();
101+for (const actionRow of actionRows) {
102+const rawLabel = typeof actionRow.label === "string" ? actionRow.label.trim() : "";
103+const label = sanitizeTerminalText(rawLabel) || "(unlabeled)";
104+let summary = summaryByLabel.get(label);
105+if (!summary) {
106+summary = { label, kept: 0, pruned: 0 };
107+summaryByLabel.set(label, summary);
108+}
109+if (actionRow.action === "keep") {
110+summary.kept += 1;
111+} else {
112+summary.pruned += 1;
113+}
114+}
115+return [...summaryByLabel.values()].toSorted((a, b) => a.label.localeCompare(b.label));
116+}
117+118+function renderLabelSummaries(params: {
119+actionRows: SessionCleanupActionRow[];
120+runtime: RuntimeEnv;
121+}) {
122+const summaries = buildLabelSummaries(params.actionRows);
123+if (summaries.length === 0) {
124+return;
125+}
126+const labelPad = Math.max(...summaries.map((summary) => summary.label.length));
127+const totalKept = summaries.reduce((total, summary) => total + summary.kept, 0);
128+const totalPruned = summaries.reduce((total, summary) => total + summary.pruned, 0);
129+params.runtime.log("");
130+params.runtime.log("Summary by Label:");
131+for (const summary of summaries) {
132+params.runtime.log(
133+`${summary.label.padEnd(labelPad)} ${summary.kept} kept, ${summary.pruned} pruned`,
134+);
135+}
136+params.runtime.log(`Total: ${totalKept} kept, ${totalPruned} pruned`);
137+}
138+90139function renderStoreDryRunPlan(params: {
91140cfg: OpenClawConfig;
92141summary: SessionCleanupSummary;
@@ -141,6 +190,7 @@ function renderStoreDryRunPlan(params: {
141190].join(" ");
142191params.runtime.log(line.trimEnd());
143192}
193+renderLabelSummaries({ actionRows: params.actionRows, runtime: params.runtime });
144194}
145195146196function renderAppliedSummaries(params: {
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。