UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

280 lines (279 loc) 11.4 kB
import { t as log } from "./logger-B8odRltZ.js"; import "./agent-harness-runtime-zRYzuZJP.js"; import { d as resolveCodexAppServerRuntimeOptions } from "./config-i2AzQJy8.js"; import { c as withCodexAppServerBindingLock, l as writeCodexAppServerBinding, o as readCodexAppServerBinding, t as CODEX_APP_SERVER_BINDING_GUARDED_REQUEST_TIMEOUT_MS } from "./session-binding-ZGWLAhUd.js"; import { u as releaseLeasedSharedCodexAppServerClient } from "./shared-client-CgnvCEQ0.js"; import { n as resolveCodexNativeExecutionBlock } from "./sandbox-guard-C8WKv-8n.js"; import { t as defaultLeasedCodexAppServerClientFactory } from "./client-factory-zEzFv7p3.js"; //#region extensions/codex/src/app-server/compact.ts /** * Native Codex app-server compaction bridge for bound OpenClaw sessions. */ const warnedIgnoredCompactionOverrides = /* @__PURE__ */ new Set(); /** * Starts native Codex compaction for a manually requested bound session, or * reports why Codex-owned automatic compaction should handle the trigger. */ async function maybeCompactCodexAppServerSession(params, options = {}) { warnIfIgnoringOpenClawCompactionOverrides(params); return compactCodexNativeThread(params, options); } function warnIfIgnoringOpenClawCompactionOverrides(params) { const ignoredConfig = readIgnoredCompactionOverridePaths(params); if (ignoredConfig.length === 0) return; const warningKey = ignoredConfig.join("\0"); if (warnedIgnoredCompactionOverrides.has(warningKey)) return; warnedIgnoredCompactionOverrides.add(warningKey); log.warn("ignoring OpenClaw compaction overrides for Codex app-server compaction; Codex uses native server-side compaction", { sessionId: params.sessionId, sessionKey: params.sessionKey, ignoredConfig }); } function readIgnoredCompactionOverridePaths(params) { const ignored = /* @__PURE__ */ new Set(); for (const entry of readCompactionOverrideEntries(params)) { const localProvider = typeof entry.record.provider === "string" ? entry.record.provider.trim() : ""; const inheritedProvider = !localProvider && typeof entry.inheritedRecord?.provider === "string" ? entry.inheritedRecord.provider.trim() : ""; const providerPath = localProvider ? `${entry.path}.compaction.provider` : inheritedProvider && entry.inheritedPath ? `${entry.inheritedPath}.compaction.provider` : void 0; if (typeof entry.record.model === "string" && entry.record.model.trim()) ignored.add(`${entry.path}.compaction.model`); if (providerPath) ignored.add(providerPath); } return [...ignored]; } function readCompactionOverrideEntries(params) { const entries = []; const defaultCompaction = readRecord(readRecord(params.config?.agents)?.defaults)?.compaction; const defaultRecord = readRecord(defaultCompaction); if (defaultRecord) entries.push({ path: "agents.defaults", record: defaultRecord }); const agentId = readAgentIdFromSessionKey(params.sessionKey ?? params.sandboxSessionKey); if (!agentId) return entries; const agentCompaction = readRecord((Array.isArray(params.config?.agents?.list) ? params.config.agents.list : []).find((agent) => { return (typeof agent?.id === "string" ? agent.id.trim().toLowerCase() : "") === agentId; }))?.compaction; const agentRecord = readRecord(agentCompaction); if (agentRecord) entries.push({ path: `agents.list.${agentId}`, record: agentRecord, inheritedRecord: defaultRecord, inheritedPath: "agents.defaults" }); return entries; } function readAgentIdFromSessionKey(sessionKey) { const parts = sessionKey?.trim().toLowerCase().split(":").filter(Boolean) ?? []; if (parts.length < 3 || parts[0] !== "agent") return; return parts[1]?.trim() || void 0; } function readRecord(value) { return value && typeof value === "object" && !Array.isArray(value) ? value : void 0; } async function compactCodexNativeThread(params, options = {}) { if (params.trigger !== "manual" && !options.allowNonManualNativeRequest) { log.info("skipping codex app-server compaction for non-manual trigger", { sessionId: params.sessionId, sessionKey: params.sessionKey, trigger: params.trigger }); return { ok: true, compacted: false, reason: "codex app-server owns automatic compaction", result: { summary: "", firstKeptEntryId: "", tokensBefore: params.currentTokenCount ?? 0, details: { backend: "codex-app-server", skipped: true, reason: "non_manual_trigger", trigger: params.trigger ?? "unknown" } } }; } const nativeExecutionBlock = resolveCodexNativeExecutionBlock({ config: params.config, sessionKey: params.sandboxSessionKey ?? params.sessionKey, sessionId: params.sessionId, surface: "native compaction" }); if (nativeExecutionBlock) return { ok: false, compacted: false, reason: nativeExecutionBlock }; const appServer = resolveCodexAppServerRuntimeOptions({ pluginConfig: options.pluginConfig }); const initialBinding = await readCodexAppServerBinding(params.sessionFile, { config: params.config }); if (!initialBinding?.threadId) return failedCodexThreadBindingCompactionResult(params, { reason: "no codex app-server thread binding", recovery: "missing_thread_binding" }); let binding = initialBinding; const requestedAuthProfileId = params.authProfileId?.trim() || void 0; if (requestedAuthProfileId && binding.authProfileId && binding.authProfileId !== requestedAuthProfileId) return { ok: false, compacted: false, reason: "auth profile mismatch for session binding" }; const shouldReleaseDefaultLease = !options.clientFactory; const client = await (options.clientFactory ?? defaultLeasedCodexAppServerClientFactory)(appServer.start, requestedAuthProfileId ?? binding.authProfileId, params.agentDir, params.config); try { if (options.allowNonManualNativeRequest) { const guardedResult = await withCodexAppServerBindingLock(params.sessionFile, async () => { const currentBinding = await readCodexAppServerBinding(params.sessionFile, { config: params.config }); if (params.abortSignal?.aborted) return { started: false, result: skippedCodexNativeCompactionResult(params, { reason: "codex app-server compaction aborted before native compaction", code: "aborted_before_native_compaction", expectedThreadId: binding.threadId, currentThreadId: currentBinding?.threadId }) }; if (!currentBinding || !isSameNativeCompactionBinding(currentBinding, binding)) { log.warn("skipping codex app-server compaction because the thread binding changed", { sessionId: params.sessionId, sessionKey: params.sessionKey, expectedThreadId: binding.threadId, currentThreadId: currentBinding?.threadId }); return { started: false, result: skippedCodexNativeCompactionResult(params, { reason: "codex app-server binding changed before native compaction", code: "binding_changed_before_native_compaction", expectedThreadId: binding.threadId, currentThreadId: currentBinding?.threadId }) }; } binding = currentBinding; await clearContextEngineProjectionBeforeNativeCompaction({ sessionId: params.sessionId, sessionFile: params.sessionFile, binding, config: params.config }); await client.request("thread/compact/start", { threadId: binding.threadId }, { timeoutMs: Math.min(appServer.requestTimeoutMs, CODEX_APP_SERVER_BINDING_GUARDED_REQUEST_TIMEOUT_MS) }); return { started: true }; }); if (!guardedResult.started) return guardedResult.result; } else await client.request("thread/compact/start", { threadId: binding.threadId }); log.info("started codex app-server compaction", { sessionId: params.sessionId, threadId: binding.threadId }); } catch (error) { if (isCodexThreadNotFoundError(error)) return failedCodexThreadBindingCompactionResult(params, { threadId: binding.threadId, reason: formatCompactionError(error), recovery: "stale_thread_binding" }); log.warn("codex app-server compaction failed", { sessionId: params.sessionId, sessionKey: params.sessionKey, threadId: binding.threadId, reason: formatCompactionError(error) }); return { ok: false, compacted: false, reason: formatCompactionError(error) }; } finally { if (shouldReleaseDefaultLease) releaseLeasedSharedCodexAppServerClient(client); } const resultDetails = { backend: "codex-app-server", threadId: binding.threadId, signal: "thread/compact/start", pending: true, ...options.allowNonManualNativeRequest ? { request: "after_context_engine", trigger: params.trigger ?? "unknown" } : {} }; return { ok: true, compacted: false, result: { summary: "", firstKeptEntryId: "", tokensBefore: params.currentTokenCount ?? 0, details: resultDetails } }; } function skippedCodexNativeCompactionResult(params, skipped) { return { ok: true, compacted: false, reason: skipped.reason, result: { summary: "", firstKeptEntryId: "", tokensBefore: params.currentTokenCount ?? 0, details: { backend: "codex-app-server", skipped: true, reason: skipped.code, request: "after_context_engine", trigger: params.trigger ?? "unknown", ...skipped.expectedThreadId ? { expectedThreadId: skipped.expectedThreadId } : {}, ...skipped.currentThreadId ? { currentThreadId: skipped.currentThreadId } : {} } } }; } function failedCodexThreadBindingCompactionResult(params, recovery) { log.warn("codex app-server compaction could not use thread binding", { sessionId: params.sessionId, sessionKey: params.sessionKey, threadId: recovery.threadId, reason: recovery.reason, recovery: recovery.recovery }); return { ok: false, compacted: false, reason: recovery.reason, failure: { reason: recovery.recovery, rawError: recovery.reason } }; } async function clearContextEngineProjectionBeforeNativeCompaction(params) { const contextEngineBinding = params.binding.contextEngine; if (!contextEngineBinding?.projection) return; await writeCodexAppServerBinding(params.sessionFile, { ...params.binding, contextEngine: { ...contextEngineBinding, projection: void 0 }, createdAt: params.binding.createdAt }, { config: params.config }); log.info("cleared codex context-engine projection before native compaction", { sessionId: params.sessionId, threadId: params.binding.threadId, previousEpoch: contextEngineBinding.projection.epoch, previousFingerprint: contextEngineBinding.projection.fingerprint }); } function isSameNativeCompactionBinding(current, expected) { return current.threadId === expected.threadId && current.authProfileId === expected.authProfileId && current.contextEngine?.engineId === expected.contextEngine?.engineId && current.contextEngine?.policyFingerprint === expected.contextEngine?.policyFingerprint && current.contextEngine?.projection?.mode === expected.contextEngine?.projection?.mode && current.contextEngine?.projection?.epoch === expected.contextEngine?.projection?.epoch && current.contextEngine?.projection?.fingerprint === expected.contextEngine?.projection?.fingerprint; } function isCodexThreadNotFoundError(error) { return formatCompactionError(error).toLowerCase().includes("thread not found"); } function formatCompactionError(error) { if (error instanceof Error) return error.message; return String(error); } //#endregion export { maybeCompactCodexAppServerSession };