import { isActiveHarnessContextEngine, type EmbeddedRunAttemptParams, } from "openclaw/plugin-sdk/agent-harness-runtime"; import { resolveCodexContextEngineProjectionMaxChars, resolveCodexContextEngineProjectionReserveTokens, } from "./context-engine-projection.js"; import type { JsonValue } from "./protocol.js"; import type { CodexAppServerContextEngineBinding, CodexAppServerContextEngineProjectionBinding, } from "./session-binding.js"; export type CodexContextEngineThreadBootstrapProjection = { mode: "thread_bootstrap"; epoch: string; fingerprint?: string; }; export function buildContextEngineBinding( params: EmbeddedRunAttemptParams, projection?: CodexContextEngineThreadBootstrapProjection, ): CodexAppServerContextEngineBinding | undefined { const contextEngine = isActiveHarnessContextEngine(params.contextEngine) ? params.contextEngine : undefined; const engineId = contextEngine?.info?.id?.trim(); if (!contextEngine || !engineId) { return undefined; } return { schemaVersion: 1, engineId, policyFingerprint: JSON.stringify({ schemaVersion: 1, engineId, engineVersion: contextEngine.info.version, ownsCompaction: contextEngine.info.ownsCompaction === true, turnMaintenanceMode: contextEngine.info.turnMaintenanceMode, citationsMode: resolveContextEngineCitationsMode(params.config), contextTokenBudget: params.contextTokenBudget, projectionMaxChars: resolveCodexContextEngineProjectionMaxChars({ contextTokenBudget: params.contextTokenBudget, reserveTokens: resolveCodexContextEngineProjectionReserveTokens({ config: params.config, }), }), }), projection: projection ? buildContextEngineProjectionBinding(projection) : undefined, }; } function buildContextEngineProjectionBinding( projection: CodexContextEngineThreadBootstrapProjection, ): CodexAppServerContextEngineProjectionBinding { return { schemaVersion: 1, mode: "thread_bootstrap", epoch: projection.epoch, fingerprint: projection.fingerprint, }; } export function isContextEngineBindingCompatible( previous: CodexAppServerContextEngineBinding | undefined, next: CodexAppServerContextEngineBinding, ): boolean { return ( previous?.schemaVersion === next.schemaVersion && previous.engineId === next.engineId && previous.policyFingerprint === next.policyFingerprint && areContextEngineProjectionBindingsCompatible(previous.projection, next.projection) ); } function areContextEngineProjectionBindingsCompatible( previous: CodexAppServerContextEngineProjectionBinding | undefined, next: CodexAppServerContextEngineProjectionBinding | undefined, ): boolean { if (!next) { return previous === undefined; } return ( previous?.schemaVersion === next.schemaVersion && previous.mode === next.mode && previous.epoch === next.epoch && previous.fingerprint === next.fingerprint ); } function resolveContextEngineCitationsMode(config: unknown): JsonValue | undefined { const rootConfig = isUnknownRecord(config) ? config : undefined; const memoryConfig = isUnknownRecord(rootConfig?.memory) ? rootConfig.memory : undefined; const citations = memoryConfig?.citations; return isJsonConfigValue(citations) ? citations : undefined; } function isUnknownRecord(value: unknown): value is Record { return Boolean(value && typeof value === "object" && !Array.isArray(value)); } function isJsonConfigValue(value: unknown): value is JsonValue { if (value === null || typeof value === "string" || typeof value === "boolean") { return true; } if (typeof value === "number") { return Number.isFinite(value); } if (Array.isArray(value)) { return value.every(isJsonConfigValue); } return isUnknownRecord(value) && Object.values(value).every(isJsonConfigValue); }