





























@@ -1,4 +1,12 @@
11import { resolveAgentDir, resolveSessionAgentId } from "../../agents/agent-scope.js";
2+import { resolveContextTokensForModel } from "../../agents/context.js";
3+import { resolveAgentHarnessPolicy } from "../../agents/harness/selection.js";
4+import {
5+OPENAI_CODEX_PROVIDER_ID,
6+OPENAI_PROVIDER_ID,
7+resolveContextConfigProviderForRuntime,
8+} from "../../agents/openai-codex-routing.js";
9+import { normalizeProviderId } from "../../agents/provider-id.js";
210import type { OpenClawConfig } from "../../config/types.openclaw.js";
311import { logVerbose } from "../../globals.js";
412import { createLazyImportLoader } from "../../shared/lazy-promise.js";
@@ -75,6 +83,99 @@ function formatCompactionReason(reason?: string): string | undefined {
7583return text;
7684}
778586+function resolveManualCompactContextTokenBudget(params: {
87+cfg: OpenClawConfig;
88+provider?: string;
89+model?: string;
90+agentId: string;
91+sessionKey: string;
92+liveContextTokens?: number;
93+persistedContextTokens?: number;
94+}): number | undefined {
95+const liveContextTokens =
96+typeof params.liveContextTokens === "number" &&
97+Number.isFinite(params.liveContextTokens) &&
98+params.liveContextTokens > 0
99+ ? Math.floor(params.liveContextTokens)
100+ : undefined;
101+102+const model = normalizeOptionalString(params.model);
103+const provider = normalizeOptionalString(params.provider);
104+if (!model || !provider) {
105+return liveContextTokens ?? resolvePersistedContextTokens(params.persistedContextTokens);
106+}
107+108+const harnessPolicy = resolveAgentHarnessPolicy({
109+ provider,
110+modelId: model,
111+config: params.cfg,
112+agentId: params.agentId,
113+sessionKey: params.sessionKey,
114+});
115+const contextConfigProvider = resolveContextConfigProviderForRuntime({
116+ provider,
117+runtimeId: harnessPolicy.runtime,
118+});
119+const configuredContextTokens = resolveContextTokensForModel({
120+cfg: params.cfg,
121+provider: contextConfigProvider,
122+model: resolveManualCompactContextModelId({
123+ provider,
124+ contextConfigProvider,
125+ model,
126+}),
127+allowAsyncLoad: false,
128+});
129+if (typeof configuredContextTokens === "number" && configuredContextTokens > 0) {
130+const configuredBudget = Math.floor(configuredContextTokens);
131+return liveContextTokens !== undefined
132+ ? Math.min(liveContextTokens, configuredBudget)
133+ : configuredBudget;
134+}
135+136+if (liveContextTokens !== undefined) {
137+return liveContextTokens;
138+}
139+140+return resolvePersistedContextTokens(params.persistedContextTokens);
141+}
142+143+function resolvePersistedContextTokens(value: number | undefined): number | undefined {
144+return typeof value === "number" && Number.isFinite(value) && value > 0
145+ ? Math.floor(value)
146+ : undefined;
147+}
148+149+function resolveManualCompactContextModelId(params: {
150+provider: string;
151+contextConfigProvider: string;
152+model: string;
153+}): string {
154+const model = params.model.trim();
155+const slashIndex = model.indexOf("/");
156+if (slashIndex <= 0) {
157+return model;
158+}
159+160+const modelProvider = normalizeProviderId(model.slice(0, slashIndex));
161+const selectedProvider = normalizeProviderId(params.provider);
162+const contextConfigProvider = normalizeProviderId(params.contextConfigProvider);
163+const modelId = model.slice(slashIndex + 1).trim();
164+if (!modelId) {
165+return model;
166+}
167+168+if (
169+modelProvider === selectedProvider ||
170+modelProvider === contextConfigProvider ||
171+(modelProvider === OPENAI_PROVIDER_ID && contextConfigProvider === OPENAI_CODEX_PROVIDER_ID)
172+) {
173+return modelId;
174+}
175+176+return model;
177+}
178+78179export const handleCompactCommand: CommandHandler = async (params) => {
79180const compactRequested =
80181params.command.commandBodyNormalized === "/compact" ||
@@ -116,6 +217,15 @@ export const handleCompactCommand: CommandHandler = async (params) => {
116217agentId: sessionAgentId,
117218isGroup: params.isGroup,
118219});
220+const contextTokenBudget = resolveManualCompactContextTokenBudget({
221+cfg: params.cfg,
222+provider: params.provider,
223+model: params.model,
224+agentId: sessionAgentId,
225+sessionKey: params.sessionKey,
226+liveContextTokens: params.contextTokens,
227+persistedContextTokens: targetSessionEntry.contextTokens,
228+});
119229const result = await runtime.compactEmbeddedPiSession({
120230 sessionId,
121231sessionKey: params.sessionKey,
@@ -143,6 +253,7 @@ export const handleCompactCommand: CommandHandler = async (params) => {
143253skillsSnapshot: targetSessionEntry.skillsSnapshot,
144254provider: params.provider,
145255model: params.model,
256+ contextTokenBudget,
146257agentHarnessId:
147258targetSessionEntry.sessionId === sessionId ? targetSessionEntry.agentHarnessId : undefined,
148259thinkLevel: params.resolvedThinkLevel ?? (await params.resolveDefaultThinkingLevel()),
@@ -186,7 +297,7 @@ export const handleCompactCommand: CommandHandler = async (params) => {
186297tokensAfterCompaction ?? runtime.resolveFreshSessionTotalTokens(targetSessionEntry);
187298const contextSummary = runtime.formatContextUsageShort(
188299typeof totalTokens === "number" && totalTokens > 0 ? totalTokens : null,
189-params.contextTokens ?? targetSessionEntry.contextTokens ?? null,
300+contextTokenBudget ?? null,
190301);
191302const reason = formatCompactionReason(result.reason);
192303const line = reason
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。