mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 05:30:42 +00:00
fix: persist embedded runtime context budget
This commit is contained in:
@@ -14,6 +14,7 @@ Docs: https://docs.openclaw.ai
|
||||
- Slack/files: return non-image `download-file` results as local file paths instead of image payloads, and include Slack file IDs in inbound file placeholders so agents can call `download-file`. Fixes #71212. Thanks @teamrazo.
|
||||
- Browser control: scope standalone loopback auth to the resolved active gateway credential and fail closed when password mode lacks a resolved password, so inactive tokens or passwords no longer authorize browser routes. Fixes #65626. (#65639) Thanks @coygeek.
|
||||
- Control UI/Codex harness: emit native Codex app-server assistant and lifecycle completion events so live webchat runs stop spinning without needing a transcript reload fallback. (#70815) Thanks @lesaai.
|
||||
- Agents/sessions: persist the runtime-resolved context budget from embedded agent runs, so Codex GPT-5.5 sessions keep the catalog/runtime context cap instead of falling back to the generic 200k status value. Fixes #71294. Thanks @tud0r.
|
||||
- Discord/replies: run `message_sending` plugin hooks for Discord reply delivery, including DM targets, so plugins can transform or cancel outbound Discord replies consistently with other channels. Fixes #59350. (#71094) Thanks @wei840222.
|
||||
- Control UI/commands: carry provider-owned thinking option ids/labels in session rows and defaults so fresh sessions show and accept dynamic modes such as `adaptive`, `xhigh`, and `max`. Fixes #71269. Thanks @Young-Khalil.
|
||||
- Image generation: make explicit `model=` overrides exact-only so failed `openai/gpt-image-2` requests no longer fall through to Gemini or other configured providers, and update `image_generate list` to mention OpenAI Codex OAuth as valid auth for `openai/gpt-image-2`. Fixes #71290 and #71231. Thanks @Young-Khalil and @steipete.
|
||||
|
||||
@@ -185,6 +185,47 @@ describe("updateSessionStoreAfterAgentRun", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("uses the runtime context budget from agent metadata instead of cold fallback", async () => {
|
||||
await withTempSessionStore(async ({ storePath }) => {
|
||||
const cfg = {} as OpenClawConfig;
|
||||
const sessionKey = "agent:main:explicit:test-runtime-context";
|
||||
const sessionId = "test-runtime-context-session";
|
||||
const sessionStore: Record<string, SessionEntry> = {
|
||||
[sessionKey]: {
|
||||
sessionId,
|
||||
updatedAt: 1,
|
||||
},
|
||||
};
|
||||
await fs.writeFile(storePath, JSON.stringify(sessionStore, null, 2));
|
||||
|
||||
const result: EmbeddedPiRunResult = {
|
||||
meta: {
|
||||
durationMs: 1,
|
||||
agentMeta: {
|
||||
sessionId,
|
||||
provider: "openai-codex",
|
||||
model: "gpt-5.5",
|
||||
contextTokens: 400_000,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
await updateSessionStoreAfterAgentRun({
|
||||
cfg,
|
||||
sessionId,
|
||||
sessionKey,
|
||||
storePath,
|
||||
sessionStore,
|
||||
defaultProvider: "openai-codex",
|
||||
defaultModel: "gpt-5.5",
|
||||
result,
|
||||
});
|
||||
|
||||
expect(sessionStore[sessionKey]?.contextTokens).toBe(400_000);
|
||||
expect(loadSessionStore(storePath)[sessionKey]?.contextTokens).toBe(400_000);
|
||||
});
|
||||
});
|
||||
|
||||
it("clears the embedded harness pin after a CLI run", async () => {
|
||||
await withTempSessionStore(async ({ storePath }) => {
|
||||
const cfg = {
|
||||
|
||||
@@ -30,6 +30,13 @@ function resolveNonNegativeNumber(value: number | undefined): number | undefined
|
||||
return typeof value === "number" && Number.isFinite(value) && value >= 0 ? value : undefined;
|
||||
}
|
||||
|
||||
function resolvePositiveInteger(value: number | undefined): number | undefined {
|
||||
if (typeof value !== "number" || !Number.isFinite(value) || value <= 0) {
|
||||
return undefined;
|
||||
}
|
||||
return Math.floor(value);
|
||||
}
|
||||
|
||||
export async function updateSessionStoreAfterAgentRun(params: {
|
||||
cfg: OpenClawConfig;
|
||||
contextTokensOverride?: number;
|
||||
@@ -62,16 +69,19 @@ export async function updateSessionStoreAfterAgentRun(params: {
|
||||
const modelUsed = result.meta.agentMeta?.model ?? fallbackModel ?? defaultModel;
|
||||
const providerUsed = result.meta.agentMeta?.provider ?? fallbackProvider ?? defaultProvider;
|
||||
const agentHarnessId = normalizeOptionalString(result.meta.agentMeta?.agentHarnessId);
|
||||
const runtimeContextTokens = resolvePositiveInteger(result.meta.agentMeta?.contextTokens);
|
||||
const contextTokens =
|
||||
typeof params.contextTokensOverride === "number" && params.contextTokensOverride > 0
|
||||
? params.contextTokensOverride
|
||||
: ((await getContextModule()).resolveContextTokensForModel({
|
||||
cfg,
|
||||
provider: providerUsed,
|
||||
model: modelUsed,
|
||||
fallbackContextTokens: DEFAULT_CONTEXT_TOKENS,
|
||||
allowAsyncLoad: false,
|
||||
}) ?? DEFAULT_CONTEXT_TOKENS);
|
||||
runtimeContextTokens !== undefined
|
||||
? runtimeContextTokens
|
||||
: typeof params.contextTokensOverride === "number" && params.contextTokensOverride > 0
|
||||
? params.contextTokensOverride
|
||||
: ((await getContextModule()).resolveContextTokensForModel({
|
||||
cfg,
|
||||
provider: providerUsed,
|
||||
model: modelUsed,
|
||||
fallbackContextTokens: DEFAULT_CONTEXT_TOKENS,
|
||||
allowAsyncLoad: false,
|
||||
}) ?? DEFAULT_CONTEXT_TOKENS);
|
||||
|
||||
const entry = sessionStore[sessionKey] ?? {
|
||||
sessionId,
|
||||
|
||||
@@ -777,6 +777,7 @@ export async function runEmbeddedPiAgent(
|
||||
sessionId: params.sessionId,
|
||||
provider,
|
||||
model: model.id,
|
||||
contextTokens: ctxInfo.tokens,
|
||||
usageAccumulator,
|
||||
lastRunPromptUsage,
|
||||
lastTurnTotal,
|
||||
@@ -1371,6 +1372,7 @@ export async function runEmbeddedPiAgent(
|
||||
sessionId: sessionIdUsed,
|
||||
provider,
|
||||
model: model.id,
|
||||
contextTokens: ctxInfo.tokens,
|
||||
usageAccumulator,
|
||||
lastRunPromptUsage,
|
||||
lastAssistant: sessionLastAssistant,
|
||||
@@ -1426,6 +1428,7 @@ export async function runEmbeddedPiAgent(
|
||||
sessionId: sessionIdUsed,
|
||||
provider,
|
||||
model: model.id,
|
||||
contextTokens: ctxInfo.tokens,
|
||||
usageAccumulator,
|
||||
lastRunPromptUsage,
|
||||
lastAssistant: sessionLastAssistant,
|
||||
@@ -1465,6 +1468,7 @@ export async function runEmbeddedPiAgent(
|
||||
sessionId: sessionIdUsed,
|
||||
provider,
|
||||
model: model.id,
|
||||
contextTokens: ctxInfo.tokens,
|
||||
usageAccumulator,
|
||||
lastRunPromptUsage,
|
||||
lastAssistant: sessionLastAssistant,
|
||||
@@ -1776,6 +1780,7 @@ export async function runEmbeddedPiAgent(
|
||||
sessionId: sessionIdUsed,
|
||||
provider: sessionLastAssistant?.provider ?? provider,
|
||||
model: sessionLastAssistant?.model ?? model.id,
|
||||
contextTokens: ctxInfo.tokens,
|
||||
agentHarnessId: attempt.agentHarnessId,
|
||||
usage: usageMeta.usage,
|
||||
lastCallUsage: usageMeta.lastCallUsage,
|
||||
|
||||
@@ -124,6 +124,7 @@ export function buildErrorAgentMeta(params: {
|
||||
sessionId: string;
|
||||
provider: string;
|
||||
model: string;
|
||||
contextTokens?: number;
|
||||
usageAccumulator: UsageAccumulator;
|
||||
lastRunPromptUsage: UsageSnapshot | undefined;
|
||||
lastAssistant?: { usage?: unknown } | null;
|
||||
@@ -139,6 +140,7 @@ export function buildErrorAgentMeta(params: {
|
||||
sessionId: params.sessionId,
|
||||
provider: params.provider,
|
||||
model: params.model,
|
||||
...(params.contextTokens ? { contextTokens: params.contextTokens } : {}),
|
||||
...(usageMeta.usage ? { usage: usageMeta.usage } : {}),
|
||||
...(usageMeta.lastCallUsage ? { lastCallUsage: usageMeta.lastCallUsage } : {}),
|
||||
...(usageMeta.promptTokens ? { promptTokens: usageMeta.promptTokens } : {}),
|
||||
|
||||
@@ -6,6 +6,7 @@ export type EmbeddedPiAgentMeta = {
|
||||
sessionId: string;
|
||||
provider: string;
|
||||
model: string;
|
||||
contextTokens?: number;
|
||||
agentHarnessId?: string;
|
||||
cliSessionBinding?: CliSessionBinding;
|
||||
compactionCount?: number;
|
||||
|
||||
@@ -1324,7 +1324,14 @@ export async function runReplyAgent(params: {
|
||||
const cliSessionBinding = isCliProvider(providerUsed, cfg)
|
||||
? runResult.meta?.agentMeta?.cliSessionBinding
|
||||
: undefined;
|
||||
const runtimeContextTokens =
|
||||
typeof runResult.meta?.agentMeta?.contextTokens === "number" &&
|
||||
Number.isFinite(runResult.meta.agentMeta.contextTokens) &&
|
||||
runResult.meta.agentMeta.contextTokens > 0
|
||||
? Math.floor(runResult.meta.agentMeta.contextTokens)
|
||||
: undefined;
|
||||
const contextTokensUsed =
|
||||
runtimeContextTokens ??
|
||||
resolveContextTokensForModel({
|
||||
cfg,
|
||||
provider: providerUsed,
|
||||
@@ -1332,7 +1339,8 @@ export async function runReplyAgent(params: {
|
||||
contextTokensOverride: agentCfgContextTokens,
|
||||
fallbackContextTokens: activeSessionEntry?.contextTokens ?? DEFAULT_CONTEXT_TOKENS,
|
||||
allowAsyncLoad: false,
|
||||
}) ?? DEFAULT_CONTEXT_TOKENS;
|
||||
}) ??
|
||||
DEFAULT_CONTEXT_TOKENS;
|
||||
|
||||
await persistRunSessionUsage({
|
||||
storePath,
|
||||
|
||||
Reference in New Issue
Block a user