import { mergeSessionEntry, setSessionRuntimeModel, type SessionEntry, updateSessionStore, } from "../../config/sessions.js"; import type { OpenClawConfig } from "../../config/types.openclaw.js"; import { setCliSessionBinding, setCliSessionId } from "../cli-session.js"; import { DEFAULT_CONTEXT_TOKENS } from "../defaults.js"; import { isCliProvider } from "../model-selection.js"; import { deriveSessionTotalTokens, hasNonzeroUsage } from "../usage.js"; type RunResult = Awaited>; let usageFormatModulePromise: Promise | undefined; let contextModulePromise: Promise | undefined; async function getUsageFormatModule() { usageFormatModulePromise ??= import("../../utils/usage-format.js"); return await usageFormatModulePromise; } async function getContextModule() { contextModulePromise ??= import("../context.js"); return await contextModulePromise; } function resolveNonNegativeNumber(value: number | undefined): number | undefined { return typeof value === "number" && Number.isFinite(value) && value >= 0 ? value : undefined; } export async function updateSessionStoreAfterAgentRun(params: { cfg: OpenClawConfig; contextTokensOverride?: number; sessionId: string; sessionKey: string; storePath: string; sessionStore: Record; defaultProvider: string; defaultModel: string; fallbackProvider?: string; fallbackModel?: string; result: RunResult; }) { const { cfg, sessionId, sessionKey, storePath, sessionStore, defaultProvider, defaultModel, fallbackProvider, fallbackModel, result, } = params; const usage = result.meta.agentMeta?.usage; const promptTokens = result.meta.agentMeta?.promptTokens; const compactionsThisRun = Math.max(0, result.meta.agentMeta?.compactionCount ?? 0); const modelUsed = result.meta.agentMeta?.model ?? fallbackModel ?? defaultModel; const providerUsed = result.meta.agentMeta?.provider ?? fallbackProvider ?? defaultProvider; const { resolveContextTokensForModel } = await getContextModule(); const contextTokens = resolveContextTokensForModel({ cfg, provider: providerUsed, model: modelUsed, contextTokensOverride: params.contextTokensOverride, fallbackContextTokens: DEFAULT_CONTEXT_TOKENS, allowAsyncLoad: false, }) ?? DEFAULT_CONTEXT_TOKENS; const entry = sessionStore[sessionKey] ?? { sessionId, updatedAt: Date.now(), }; const next: SessionEntry = { ...entry, sessionId, updatedAt: Date.now(), contextTokens, }; setSessionRuntimeModel(next, { provider: providerUsed, model: modelUsed, }); if (isCliProvider(providerUsed, cfg)) { const cliSessionBinding = result.meta.agentMeta?.cliSessionBinding; if (cliSessionBinding?.sessionId?.trim()) { setCliSessionBinding(next, providerUsed, cliSessionBinding); } else { const cliSessionId = result.meta.agentMeta?.sessionId?.trim(); if (cliSessionId) { setCliSessionId(next, providerUsed, cliSessionId); } } } next.abortedLastRun = result.meta.aborted ?? false; if (result.meta.systemPromptReport) { next.systemPromptReport = result.meta.systemPromptReport; } if (hasNonzeroUsage(usage)) { const { estimateUsageCost, resolveModelCostConfig } = await getUsageFormatModule(); const input = usage.input ?? 0; const output = usage.output ?? 0; const totalTokens = deriveSessionTotalTokens({ usage: promptTokens ? undefined : usage, contextTokens, promptTokens, }); const runEstimatedCostUsd = resolveNonNegativeNumber( estimateUsageCost({ usage, cost: resolveModelCostConfig({ provider: providerUsed, model: modelUsed, config: cfg, }), }), ); next.inputTokens = input; next.outputTokens = output; if (typeof totalTokens === "number" && Number.isFinite(totalTokens) && totalTokens > 0) { next.totalTokens = totalTokens; next.totalTokensFresh = true; } else { next.totalTokens = undefined; next.totalTokensFresh = false; } next.cacheRead = usage.cacheRead ?? 0; next.cacheWrite = usage.cacheWrite ?? 0; if (runEstimatedCostUsd !== undefined) { next.estimatedCostUsd = (resolveNonNegativeNumber(entry.estimatedCostUsd) ?? 0) + runEstimatedCostUsd; } } if (compactionsThisRun > 0) { next.compactionCount = (entry.compactionCount ?? 0) + compactionsThisRun; } const persisted = await updateSessionStore(storePath, (store) => { const merged = mergeSessionEntry(store[sessionKey], next); store[sessionKey] = merged; return merged; }); sessionStore[sessionKey] = persisted; }